#!/usr/bin/python z = "\r\n" DEFAULT_HOST = "irc.freenode.net" DEFAULT_PORT = 6667 DEFAULT_NICK = "Myrkur" DEfAULT_USER = "Myrkur" import re import socket import sys import os class Types: pass types = Types() types.MSG = 1 types.NOTICE = 2 types.JOIN = 3 types.PART = 4 types.KICK = 5 types.CQUIT = 6 types.MODE = 7 types.TOPIC = 8 # unknown modes get 1000 added, that is: a message like :yab.ath.cx 001 Myrkur :Welcome to the Internet Relay Network Myrkur gets mode 1001 class IRCdata: def __init__(self): self.host = "" self.port = 6667 self.nick = "" self.user = "" self.real = "" self.server_pass = "" self.nickserv_pass = "" self.autoexec = [] def check(self): if (self.host == ""): self.host = DEFAULT_HOST if (self.port == 0): self.port = DEFAULT_PORT if (self.nick == ""): self.nick = DEFAULT_NICK if (self.user == ""): self.user = DEFAULT_USER if (self.server_pass == ""): self.use_server_pass = False else: self.use_server_pass = True if (self.nickserv_pass == ""): self.use_nickserv_pass = False else: self.use_nickserv_pass = True def next_nick(self): self.nick = self.nick + "_" return self.nick def normalize(stringle): return stringle.strip().lower() def userathost(a, b): "Concatenates a and b to a '(user@host) ' combination, adds a blank at the end." if a == None or b == None: return "" else: return "("+str(a)+"@"+str(b)+") " class IRC: def __init__(self, data): data.check() self.user = data self.connected = False self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.queue = [] self.parsedqueue = [] self.channels = {} self.moddir = "." # we assume . till somebody tells us something different # TODO: Log file please ##################################### # MODULES # ##################################### self.modlist = {} def moddir(self, moddir): if os.path.exists(moddir): self.moddir = moddir def send(self, data, selfconnected = False): if (not selfconnected and not self.connected): return False data = data + z self.socket.send (data) return True def raw_recv(self, bufsiz = 1024): #x[-1] data = self.socket.recv(bufsiz) if (len(data) == 0): self.connected = False self.do_quit("poof.") c = 1 while (data[-1] != '\n'): data = data + self.socket.recv(bufsiz) print data c += 1 return data def recv(self, bufsiz = 1024): if (not self.connected): return None data = self.raw_recv (bufsiz) data2 = data.splitlines() for i in data2: if (i.find("PING") == 0): pong = i.split()[1] # if the server sends PING :12345, we return PONG :12345, if it sends PING 12345, we return PONG 12345. self.socket.send("PONG "+pong+z) else: self.queue.append(i.strip()) # We don't enqueue pings, because they're vital. - strip() is trim(), btw. def parse_queue (self): while len(self.queue) > 0: i = self.queue.pop(0) if (i[0] == ':'): print " "+i # see what it is xsplices = re.findall(':(.*?) .*', i) ysplices = () if len(xsplices) > 0: ysplices = xsplices[0] else: continue zsplices = re.findall('(.*?)!(.*?)@(.*)', ysplices) if (len(zsplices) > 0): splices = zsplices[0] else: splices = (ysplices, None, None) if (len(splices) > 2): nick = splices[0] user = splices[1] host = splices[2] elif (len(splices) < 1): nick = ysplices[0] user = None host = None else: nick = splices[0] user = None host = None sender = i.split()[0] rest = i[len(sender):].strip() # now we've got something like "MODE #yab +o Xjs" what = rest.split()[0].lower() # I convert all the args to lower case, it's easier later. rest = rest[len(what):].strip() if (what == "quit"): mesg = rest[1:] # print nick+" "+userathost(user,host)+"quit (Reason: "+mesg+")!" continue chan = rest.split()[0].lower() mesg = rest[len(chan):].strip() if (len(mesg) > 0 and mesg[0] == ':'): mesg = mesg[1:] # print nick+" "+userathost(user,host)+"did a "+what+" on "+chan, if (mesg != ""): # print "with message "+mesg pass else: # print pass if (what == "join" and nick == self.user.nick): self.channels[chan] = True if (what == "part" and nick == self.user.nick): self.channels[chan] = False t = 0 pq = (0, None) # append to parsedqueue if what == "privmsg": t = types.MSG sender = (nick, user, host) pq = (t, (sender, chan, mesg)) # the parser knows how to handle that elif what == "notice": t = types.NOTICE sender = (nick, user, host) # destination is always "me" pq = (t, (sender, mesg)) elif what == "join": t = types.JOIN sender = (nick, user, host) pq = (t, (sender, chan)) # there are no join messages elif what == "part": t = types.PART sender = (nick, user, host) pq = (t, (sender, chan, mesg)) elif what == "kick": t = types.KICK sender = (nick, user, host) whom = mesg.split()[0] mesg = mesg[len(whom):].strip() mesg = mesg[1:] pq = (t, (sender, whom, chan, mesg)) elif what == "quit": t = types.CQUIT sender = (nick, user, host) pq = (t, (sender, chan, mesg)) elif what == "mode": t = types.MODE sender = (nick, user, host) info = mesg.split() mode = info.pop(0) whom = info # info[0] is now away pq = (t, (sender, mode, chan, whom)) elif what == "topic": t = types.TOPIC sender = (nick, user, host) pq = (t, (sender, chan, mesg)) else: t = int(what)+1000 # to be parsed by an unknown authority sender = i.split()[0] raw_args = i[len(sender):].strip() x = raw_args.find(":") mesg = None if (x > -1): mesg = raw_args[x+1:] # +1 because of the : raw_args2 = raw_args[:x].strip() else: raw_args2 = raw_args args = raw_args2.split() args.pop(0) args.pop(0) # it's always our nickname I think if mesg != None: args.append(mesg) pq = (t, args) # important: args is a list, not a tuple (because it's easier to handle) self.parsedqueue.append(pq) print pq #what = splices[3] #chan = splices[4] #mesg = splices[5] #reuse it... else: print i def connect(self): """Connect to the server and enter main loop""" if (not self.connected): self.socket.connect((self.user.host, self.user.port)) #Auth. init = self.raw_recv() # reuse it if you like #nick first self.send ("NICK "+self.user.nick, True) self.send ("USER "+self.user.user+" 0 * :"+self.user.real, True) inuse = self.raw_recv() while (len(re.findall(':.* 433 AUTH '+self.user.nick+' :.*', inuse)) > 0): self.send ("NICK "+self.user.next_nick(), True) self.send ("USER "+self.user.user+" 0 * :"+self.user.real, True) inuse = self.raw_recv() # now inuse should contain a ping inuse2 = inuse.splitlines() # go through the lines for i in inuse2: if (i.find("PING") == 0): pong = i.split()[1] # if the server sends PING :12345, we return PONG :12345, if it sends PING 12345, we return PONG 12345. self.socket.send("PONG "+pong+z) else: self.queue.append(i.strip()) # okay. Now auth is over. self.connected = True # Server/Nickserv auth if (self.user.server_pass != ""): self.send("PASS "+self.user.server_pass) if (self.user.nickserv_pass != ""): self.send("PRIVMSG NickServ :IDENTIFY "+self.user.nickserv_pass) # Autoexec functions for i in self.user.autoexec: self.send(i) # We expect the user to enter serious values here. ;-) while True: try: self.recv() self.parse_queue() except KeyboardInterrupt: self.do_quit ("KeyboardInterrupt") # COMMANDS! def do_quit(self, s = "MyrkurBot quit because he was told to"): if (self.connected): self.send("QUIT :"+s) self.connected = False sys.exit() return None def msg (self, chan, message): self.send ("PRIVMSG "+chan.strip()+" :"+message) def is_joined (self, chan): chan = normalize(chan) return self.channels.has_key(chan) and self.channels[chan] def join (self, chan): #is_joined is set by the parse_queue() function chan = normalize(chan) if (not self.is_joined(chan)): self.send ("JOIN "+chan) def part (self, chan): chan = normalize(chan) if (self.is_joined(chan)): self.send("PART "+chan) # MODULES! Now it's getting funny. def addmod (self, name): # name is the modname, and it's saved in the database under this name. return None # this is not finished if self.modlist.has_key(name): themod = reload (self.modlist[name]) #test if it's serious for i in dir(themod): if i == "parse": # gotta be a class. if str(type(themod.parse)) == "": pass self.modlist[name] = themod return True else: themod = None if self.moddir == ".": try: themod = __import__(name) # we hope that works ;_) except ImportError: return False