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.
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.
#!/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.
No comments:
Post a Comment