Sunday, July 22, 2012

IRC Bot (logbot)

After my first post on IRC bots, I decided to make a bot for logging a chat that was a bit better designed than the one I posted. It is far from perfect, however it will write formatted logs to a file for you, won't sit around if it is kicked and if it sees a message it cannot format, it will log the raw data. Also to keep in mind, it only joins a channel ofter the motd finishes with a certain number code. If there is no motd or it does not send the right number code it won't join. The reason for this is I just wrote the thing today and don't have the time to check all possibilities. So use it as a skeleton and develop on it further. Or ignore it and move on with your day. All descriptions will be commented in the code.

#!/usr/bin/env python
# Import all the packages we will be using
import logging
import socket
import ssl
import os
import sys
import re

# Some global variables for configuration
HOST = "irc.69megabytes.com"
PORT = 6697
NICK = "PyLogBot46"
CHANNEL = "#allthefallen"
SSL = True
FLOG = os.path.join(sys.path[0], "ircbot.log")
PWD = "supersecretpassword"
# This is for use later on to know when to join the channel and do it only once
notInChan = True

# Regex parsing of a standard IRC message
def parse(data):
    temp=re.match(r"^(:(?P<prefix>\S+) )?(?P<command>\S+)( (?!:)(?P<params>.+?))?( :(?P<trail>.+))?$", data)
    if temp:
        temp=temp.groupdict()
    return temp

# Where the magic happens
if __name__ == '__main__':
    # Set up the logging format
    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(message)s', datefmt="%Y-%d-%m (%H:%M:%S %Z)", filename=FLOG)
    # Message format strings so we can easily alter them
    format_privmsg = "{1} <{0}>: {2}"
    format_notice = "(notice) <{0}>: {1}"
    format_join = "{0} has entered {1}"
    format_nick = "{0} is now known as {1}"
    format_mode = "{0} set mode {1}"
    format_modes = "Mode set {0}"
    format_kick = "{0} kicked from {1} by {2} ({3})"
    format_srvmsg = "-!- {0} {1} {2}"
    try:
        # Create socket
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # Set up SSL if necessary
        if SSL:
            s = ssl.wrap_socket(s)

        # Connect to the server
        s.connect((HOST, PORT))

        # Authenticate
        s.send("NICK " + NICK + "\r\n")
        s.send("USER " + NICK + " www.anarchy46.net PY :Python LogBot\r\n")

        # Main loop
        while 1:
            # Reset information received and processed
            data = ""
            info = {}
            # Retreive data 1 byte at a time, stop at newline
            while data.find("\n") == -1:
                data += s.recv(1)
            # Remove unnecessary whitespace
            data = data.strip()
            # Parse the raw data
            info = parse(data)
            # Privmsg handler
            if info['command'] == "PRIVMSG":
                logging.info(format_privmsg.format(info['prefix'], info['params'], info['trail']))
            # Notices
            elif info['command'] == "NOTICE":
                # If to self from someone
                if info['params'] == NICK:
                    # To quit properly
                    if info['trail'] == PWD:
                        s.send("QUIT :Done logging.\r\n")
                        break
                    # So we don't log the password
                    logging.info(format_notice.format(info['prefix'], info['trail']))
                # Server notices
                else:
                    logging.info(format_srvmsg.format(info['prefix'], info['params'], info['trail']))
            # Respond to server pings (no need to log)
            elif info['command'] == "PING":
                s.send("PONG " + info['trail'] + "\r\n")
            # Someone joins
            elif info['command'] == "JOIN":
                logging.info(format_join.format(info['prefix'], info['trail']))
            # Someone changes nick
            elif info['command'] == "NICK":
                logging.info(format_join.format(info['prefix'], info['trail']))
            # Modes
            elif info['command'] == "MODE":
                # Normal mode changes
                if info['trail']:
                    logging.info(format_modes.format(info['trail']))
                # Modes for self (set by server)
                else:
                    logging.info(format_mode.format(info['prefix'], info['params']))
            # Someone gets kicked
            elif info['command'] == "KICK":
                # Quit if it's the bot
                temp = info['params'].split(' ')
                if temp[1] == NICK:
                    logging.info("Kicked from channel.")
                    s.send("QUIT :Bye bye...\r\n")
                    break
                # Log everyone else being kicked :D
                else:
                    logging.info(format_kick.format(temp[0], temp[1], info['prefix'], info['trail'])
            # Special server messages
            elif info['command'].isdigit():
                if info['trail']:
                    logging.info(format_srvmsg.format(info['prefix'], info['command'], info['trail']))
                else:
                    logging.info(format_srvmsg.format(info['prefix'], info['command'], info['params']))
                # End of MOTD
                if notInChan and info['command'] == "376":
                    s.send("JOIN " + CHANNEL + "\r\n")
                    notInChan = False
            else:
                logging.info(data)
    # Abort!
    except socket.error as err:
        logging.critical("Socket error: " + str(err.errno))
        sys.exit(err.errno)

It's not the prettiest of code, but it works. When launching from the command line, make sure to add an ampersand (&) at the end so you don't need to wait for it or leave a window open with it running. Enjoy, and remember this thing only took me a couple of hours to write and sort out some formats.

1 comment:

  1. Beyond that, one of Greenfield’s specialties is web habit. Interestingly, he mentioned there are similarities between that and gambling habit. He likened the web to a on line casino in its own means and predicted internet-based gambling would produce more problems going forward outcome of|as a result of} it’s adding one potent ingredient to a different. I needed to cease myself from betting on the team I actually care about in the playoffs outcome of|as a result of} their video games give me enough nervousness as is. And, in fact, downside gambling doesn’t exist in a vacuum — these problems affect on} the gamblers’ partners, youngsters, and 카지노사이트 different loved ones, too.

    ReplyDelete

Tag Cloud

.NET (2) A+ (5) ad ds (1) addon (4) Android (4) anonymous functions (1) application (9) arduino (1) artificial intelligence (1) backup (1) bash (6) camera (2) certifications (3) comptia (5) css (2) customize (11) encryption (3) error (13) exploit (5) ftp (1) funny (4) gadget (4) games (3) GUI (5) hardware (16) haskell (6) help (14) HTML (3) imaging (2) irc (1) it (1) java (2) javascript (13) jobs (1) Linux (19) lua (1) Mac (4) malware (1) math (6) msp (1) network (13) perl (2) php (3) plugin (2) powershell (8) privacy (2) programming (24) python (10) radio (2) regex (3) repair (2) security (16) sound (2) speakers (2) ssh (1) story (5) Techs from the Crypt (5) telnet (1) tools (13) troubleshooting (11) tutorial (9) Ubuntu (4) Unix (2) virtualization (2) web design (6) Windows (16) world of warcraft (1) wow (1) wx (1)