[4] IRC Bot

   |   10 minute read   |   Using 1961 words

By Gitgood

Writing an IRC bot in Python (3.5)

May 6, 2016

Abstract
This article will hopefully introduce the reader to IRC, basic sockets programming in Python and general IRC bot creation. IRC has been an incredibly popular and prominent mode of communication in the cyberpunk/programming/tech literate communities from the early 90’s, and because of this many great communities have and continue to flourish and mature on these networks. Because of the simplistic nature of IRC, the creation of “bots” that can provide useful functions in IRC channels is surprisingly trivial and easy to pick up. Also, I am aware the code formatting is less than perfect so if you just want to grab the code, here’s a link: http://pastie.org/10826422

What is IRC? Why would you need a bot?

What is IRC?

Developed in late 1988, IRC stands for “Internet Relay Chat” and is a massively popular communication platform.

IRC lets users connect to a server and communicate with other connected users (usually through some IRC client) in real time, even allowing the transfer of files. IRCP (Internet Relay Chat Protocol) is the application layer protocol which helps facilitate the communication of these IRC clients.

When connected to an IRC server, a user can issue various different commands. A few examples of these are:1

Command         What is does
/away MESSAGE   Leaves a message explaining why you're away.
/help           Displays a list of all commands.
/join CHANNEL   Joins #channel
/message USER   Sends message to USER
/nick NICKNAME  Sets user's nickname to NICKNAME 

Once connected to a server, a user still needs to join a channel before they can begin chatting. Channels can be described as chatrooms that people can join and leave at will. So, for example, if a user wants to connect to freenode’s #python channel they would first join irc.freenode.net using their IRC client and after all the MOTD (Message of the day) text would type /join #python. From here the user is free to chat away to the other users connected to #python. IRC gained massive popularity in the 90’s in the programming/hacking/technology-literate subcultures. “In the autumn year 2000, EFnet has some 50,000 users and IRCnet 70,000."2 As it stands today, the largest IRC networks currently are:

  • IRCnet - 38716 users.
  • QuakeNet - 34879 users.
  • EFnet - 22790 users.
  • Rizon - 21030 users.3
LAINCHAN IRC Total IRC abuse.

Because of the massive popularity of IRC, if you have an interest then chances are there is a community for it.

Bots? Why?

IRC itself is fairly simple and as a result doesn’t come jam packed with features. People found that due to the simplistic nature of IRC, computer programs could easily be written to provide certain features that IRC lacks. For example, a bot could be written to emulate a magic 8-ball. It would listen on a channel for a command such as !8ball [QUESTION], and once found would choose a random 8-ball phrase and send it back to the chat. It would look something like:

<gitgood>   !8ball will I die soon?
<8ballbot>  Without a doubt.
<gitgood>   damn.

This, of course, is a very simple example but you get the gist. This is the bot we are going to be writing.

Writing the IRC bot

Now that the basics of IRC is out of the way, we can now get to implementing the bot.

Setting up the bot

As mentioned previously, we’re going to be writing this magic 8ball bot in Python 3.5 so some Python experience is suggested. Firstly, we need to import two modules. These are “socket” and “random”. The socket module will provide us with network communication, and the random module will be used to select a random magic 8ball phrase. If you don’t know much about sockets, I suggest reading:

  • import socket
  • import random

After this we’re going to need the magic 8-ball phrases in a list. Then, when the time comes, we can choose a random phrase from the list. Magic 8-balls typically have 20 standard phrases. 10 of these phrases are positive (Yes), 5 of these are neutral (Ask again later) and the remaining 5 are negative (My sources say no). Here is the finished list:

phrases = [
    "It is certain.", #First ten phrases are "positive"
    "It is decidedly so.",
    "Without a doubt.",
    "Yes, definitely.",
    "You may rely on it.",
    "As I see it, yes.",
    "Most likely.",
    "Outlook good.",
    "Yes.",
    "Signs point to yes.",
    "Reply hazy, try again.", #Five "neutral" phrases.
    "Ask again later.",
    "Better not tell you now.",
    "Cannot predict now.",
    "Concentrate and ask again.",
    "Don't count on it.", #Five "negative" phrases.
    "My reply is no.",
    "My sources say no.",
    "Outlook not so good.",
    "Very doubtful."
    ]

Now that the phrases array has been made, we need to create some variables to store the IRC server, channel and bot nickname.

server = "irc.freenode.net"
channel = "#8ballbottest"
nickname = "magicbottest"

The next thing that needs to be done is the creation of the socket instance, the connection to the IRC server and the sending of some initial information.

irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM).
irc.connect((server, 6667))
irc.send(("USER " + nickname + " " + nickname + " " + nickname + "
    :Magicbot\r\n").encode(encoding="UTF-8"))
irc.send(("NICK " + nickname +"\r\n").encode(encoding="UTF-8"))
irc.send(("JOIN " + channel +"\r\n").encode(encoding="UTF-8"))

The first line creates a socket instance with two parameters. The first parameter is socket.AF_INET, and this refers to the IPv4 address family. The second parameter is socket.SOCK_STREAM, and this “Provides sequenced, reliable, two-way, connection-based byte streams."4

The next line uses the socket to connect to the freenode server on port 6667. “The well known TCP port for IRC traffic is 6667”5

After connecting to the server there are three lines that are for sending data to the server. The first line sends the USER command in the format:

USER username hostname servername :realname

MICROSOFT MALWARE PROTECTION CENTER
“No one could have anticipated all the ways that Internet Relay Chat (IRC) would eventually be used when it was ‘created’ in Finland during the late 1980s. People really started picking up on IRC in the early 1990s.”

“The username is the user part in your user@host hostmask that appears on IRC, which shows where your connection comes from. The realname is used to populate the real name field that appears when someone uses the WHOIS command on your nick."6

Making the bot respond

Now that the bot has connected to the IRC server and joined the channel, it now needs a way of getting information from the channel and parsing that for use. Fortunately, this is very easy too.

IRC USAGE
Peaking in 2005 with a total of around 450,000 users across the top 6 IRC networks, falling to approximately 300,000 by 2012. Freenode being the only network enjoying a sustained increase in user base.

while True:
        #Receive data from the socket, and decode it.
        received = irc.recv(2048).decode("UTF-8")
        print(received)
        #print(bytes(received, "UTF-8"))
        #If the server sends a PING.
        if received.startswith("PING"):
                #Respond with a PONG to prevent timing out.
                irc.send(("PONG " + received.split()[1] + "\r\n").encode())
                print("Ponged")

        if ":!8ball" in received:
            #Splits the received response in to a list with two
        elements. [0] is what was before the !8ball, and [1] is what was after.
            #It then gets what was after "!8ball", and calls .strip() on this 
            #string to remove any trailing whitespace characters such as "\r" and "\n"
        question = received.split(":!8ball")[1].strip()
            #If there has been a statement after !8ball such as
        "!8ball am I going to die?"
            if question != "":
                    #Then send a a random phrase from the phrases array to the channel.
                    irc.send(("PRIVMSG " + channel + " :" + 
                        random.choice(phrases) + " \r\n").encode())

Everything is in an infinite loop as we always want to be receiving data and parsing it. We receive at most 2048 bytes from the connection, and then decode it to UTF-8. After decoding the received data we then print it. Errors may occur here if the channel the IRC bot is joining has Unicode characters in it. If so, just uncomment the next line instead. To ensure that the client hasn’t timed out, the server will occasionally “PING” the client. When a message that starts with “PING” is found, then we send back the appropriate “PONG” to let the server know we’re still connected. If “!8ball” is in the recieved data, then we can assume someone has tried to summon the magic 8ball bot. We then get the text after “!8ball”. If the question is equal to “”, then no question has been asked after “!8ball” and therefor no response should be given. If there is a message after “!8ball”, then send a random choice from the phrases array to the IRC channel.

Complete code:

import socket
import random
phrases = [
    "It is certain.", #First ten phrases are "positive"
    "It is decidedly so.",
    "Without a doubt.",
    "Yes, definitely.",
    "You may rely on it.",
    "As I see it, yes.",
    "Most likely.",
    "Outlook good.",
    "Yes.",
    "Signs point to yes.",
    "Reply hazy, try again.", #Five "neutral" phrases.
    "Ask again later.",
    "Better not tell you now.",
    "Cannot predict now.",
    "Concentrate and ask again.",
    "Don't count on it.", #Five "negative" phrases.
    "My reply is no.",
    "My sources say no.",
    "Outlook not so good.",
    "Very doubtful."
]

server = "irc.freenode.net" #We're connecting to the freenode IRC server.
channel = "#8ballbottest" #A (probably) empty channel for testing the bot.
nickname = "magicbottest" #The nickname of the bot.

irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #Create a socket instance.

irc.connect((server, 6667))
irc.send(("USER " + nickname + " " + nickname + " " + nickname + "
:Magicbot\r\n").encode(encoding="UTF-8"))
irc.send(("NICK " + nickname +"\r\n").encode(encoding="UTF-8"))
irc.send(("JOIN " + channel +"\r\n").encode(encoding="UTF-8"))

#Infinite loop.
while True:
    #Receive data from the socket, and decode it.
    recieved = irc.recv(2048).decode("UTF-8")
    print(recieved)
    #print(bytes(recieved, "UTF-8"))
    #If the server sends a PING.
    if recieved.startswith("PING"):
        #Respond with a PONG to prevent timing out.
        irc.send(("PONG " + recieved.split()[1] + "\r\n").encode())
        print("Ponged")

    if ":!8ball" in recieved:
        #Splits the recieved response in to a list with two elements. [0]
is what was before the !8ball, and [1] is what was after.
        #It then gets what was after "!8ball", and calls .strip() on this
string to remove any trailing whitespace characters such as "\r" and "\n"
        question = recieved.split(":!8ball")[1].strip()
        #If there has been a statement after !8ball such as "!8ball am I going to die?"
        if question != "":
            #Then send a a random phrase from the phrases array to the channel.
            irc.send(("PRIVMSG " + channel + " :" + random.choice(phrases) + " \r\n").encode())

How else can they be used?

I’m glad you asked! One very malicious use for IRC bots is the commanding of botnets. As you probably know, botnets are a large network of infected “zombie” computers that can be controlled (often maliciously). For instance, say you’ve a network of 10000 bots, and you command them to spam a webhost with requests then that could possibly take that server offline (depending on how large they are).

How would you command this server? You could directly send the command to each and every “zombie”, but that doesn’t seem very efficient. What you could do however, is include some IRC bot code not unlike the code we wrote previously so that when a computer is infected the virus automatically connects to a certain server/channel. Once it has connected, it can then listen for commands. An example might be:

<1337man> !ddos https://www.volafile.io

Each one of these infected computers would see this command, and then execute some function that would flood the link with requests.


  1. Unknown. Irc information… https://www.ircbeginner.com/ircinfo/irc-commands.html, 2013. ↩︎

  2. D. Stenberg. History of irc (internet relay chat). https://daniel.haxx.se/irchistory.html, 2011. ↩︎

  3. A. Gelhausen. Irc networks top 100. https://irc.netsplit.de/networks/top100.php, 2016. ↩︎

  4. M. Kerrisk. Socket(2) ­ linux programmer’s manual. https://man7.org/linux/man­pages/man2/socket.2.html, 2015. ↩︎

  5. L. MikeDuigou. Internet relay chat (irc). https://wiki.wireshark.org/IRC, 2008. ↩︎

  6. Caf. What is the difference between the nick, username, and real name in irc, and what is the password? http://stackoverflow.com/questions/31666247/what-is-the-difference-between-the-nick-username-and-real-name-in-irc-and-wha ↩︎