diff --git a/botcmd.py b/botcmd.py index 758bbb1..e5de8e5 100644 --- a/botcmd.py +++ b/botcmd.py @@ -4,113 +4,163 @@ import random import re import time -concmd=['/q','/lt','/st','/lg'] +concmd=['/q', '/lt', '/st', '/lg'] -blacklist=['bslsk05'] +blacklist = ['bslsk05'] -doctor=eliza.eliza() -trusted=[] -trustedlock=threading.Lock() -gods=[] -godslock=threading.Lock() -msgs={} -msglock=threading.Lock() -authcheck={} -authchecklock=threading.Lock() +doctor = eliza.eliza() + +trusted = [] +trustedlock = threading.Lock() +gods = [] +godslock = threading.Lock() + +msgs = {} +msgslock = threading.Lock() + +authcheck = {} +authchecklock = threading.Lock() die_expr=re.compile("#[0-9]*d([0-9]+|%)") class Cron(threading.Thread): def __init__(self): - self.timedjobs=[] - self.timedjobslock=threading.Lock() - self.cronctrl=[] - self.cronctrllock=threading.Lock() + self.timedjobs = [] + self.timedjobslock = threading.Lock() + self.cronctrl = [] + self.cronctrllock = threading.Lock() threading.Thread.__init__(self) + def queuejob(self, time, fn): self.timedjobslock.acquire() self.timedjobs.append((time, fn)) self.timedjobslock.release() + def ctrl(self, cmd): self.cronctrllock.acquire() self.cronctrl.append(cmd) self.cronctrllock.release() + def run(self): - run=True + run = True while run: time.sleep(1) # Accuracy doesn't need to be high self.cronctrllock.acquire() for cmd in self.cronctrl: - if cmd=='QUIT': - run=False + if cmd == 'QUIT': + run = False self.cronctrl=[] self.cronctrllock.release() self.timedjobslock.acquire() - self.timedjobs=map((lambda (time,fn): (time-1,fn)), self.timedjobs) - torun=map((lambda (time,fn): fn), filter((lambda (time,fn): time<=0), self.timedjobs)) - self.timedjobs=filter((lambda (time,fn): time>0), self.timedjobs) + self.timedjobs = map((lambda (time, fn): (time-1, fn)), self.timedjobs) + torun = map((lambda (time, fn): fn), filter((lambda (time, fn): time<=0), self.timedjobs)) + self.timedjobs = filter((lambda (time, fn): time>0), self.timedjobs) self.timedjobslock.release() for fn in torun: fn() -msglock.acquire() -f=open('msgs.txt','r') -for line in f: - while len(line)>0 and line[-1]=='\n': line=line[:-1] - if len(line)>0: - receiver,sender,msg=line.split('\t') - if receiver not in msgs: - msgs[receiver]=[] - msgs[receiver].append((sender,msg)) -f.close() -msglock.release() - cron=Cron() cron.start() +def loadmessages(): + global msgs, msgslock + + msgslock.acquire() + f = open('msgs.txt', 'r') + + for line in f: + while len(line) > 0 and line[-1] == '\n': + line = line[:-1] + if len(line) > 0: + receiver, sender, msg = line.split('\t') + if receiver not in msgs: + msgs[receiver] = [] + msgs[receiver].append((sender, msg)) + + f.close() + msgslock.release() + +def savemessages(): + global msgs, msgslock + + msgslock.acquire() + f=open('msgs.txt', 'w') + + for receiver in msgs: + for sender, msg in msgs[receiver]: + f.write('%s\t%s\t%s\n' % (receiver, sender, msg)) + + f.close() + msgslock.release() + +loadmessages() + def addtrusted(nick): + global trusted, trustedlock + trustedlock.acquire() + if nick not in trusted: trusted.append(nick) + trustedlock.release() def rmtrusted(nick): + global trusted, trustedlock + trustedlock.acquire() + if nick in trusted: trusted.remove(nick) + trustedlock.release() def loadtrusted(): + global trusted, trustedlock + trustedlock.acquire() - while len(trusted)>0: trusted.pop() #I'm really sorry but trusted=[] created trusted as local variable + trusted = [] trustedlock.release() - f=open('trusted.txt','r') + + f=open('trusted.txt', 'r') + for line in f: - while len(line)>0 and line[-1]=='\n': line=line[:-1] - if len(line)>0: + while len(line) > 0 and line[-1] == '\n': + line = line[:-1] + if len(line) > 0: addtrusted(line) + f.close() def loadgods(): + global gods, godslock + godslock.acquire() - while len(gods)>0: gods.pop() #See above - f=open('gods.txt','r') + gods = [] + f=open('gods.txt', 'r') + for line in f: - while len(line)>0 and line[-1]=='\n': line=line[:-1] - if len(line)>0: + while len(line) > 0 and line[-1] == '\n': + line = line[:-1] + if len(line) > 0: gods.append(line) addtrusted(line) + f.close() godslock.release() def savetrusted(): + global trusted, trustedlock + trustedlock.acquire() - f=open('trusted.txt','w') + f=open('trusted.txt', 'w') + for i in trusted: f.write(i+'\n') + f.close trustedlock.release() @@ -120,11 +170,11 @@ loadgods() def chmode(irc, chan, nick, mode, args): if args == ['']: if isauthorized(irc, nick): - irc.send('MODE %s %s %s'%(chan,mode,nick)) + irc.send('MODE %s %s %s' % (chan, mode, nick)) else: for name in args: if isauthorized(irc, nick): - irc.send('MODE %s %s %s'%(chan,mode,name)) + irc.send('MODE %s %s %s' % (chan, mode, name)) def istrusted(nick): trustedlock.acquire() @@ -137,27 +187,32 @@ def istrusted(nick): def initauthcheck(nick): global authcheck, authchecklock + authchecklock.acquire() - authcheck[nick]=None + authcheck[nick] = None authchecklock.release() def setauthcheckstate(nick, state): global authcheck, authchecklock + authchecklock.acquire() if nick in authcheck: - authcheck[nick]=state + authcheck[nick] = state authchecklock.release() def getauthcheckstate(nick): global authcheck, authchecklock + authchecklock.acquire() if nick in authcheck: - state=authcheck[nick] + state = authcheck[nick] authchecklock.release() + return state def removeauthcheck(nick): global authcheck, authchecklock + authchecklock.acquire() if nick in authcheck: del authcheck[nick] @@ -168,12 +223,12 @@ def isauthorized(irc, nick): return False initauthcheck(nick) - irc.msg('NickServ', 'acc '+nick) + irc.msg('NickServ', 'acc ' + nick) cron.queuejob(5, (lambda : setauthcheckstate(nick, False))) - state=None - while state==None: - state=getauthcheckstate(nick) + state = None + while state == None: + state = getauthcheckstate(nick) time.sleep(0.1) removeauthcheck(nick) @@ -183,7 +238,7 @@ class ArgsfmtError(Exception): def __init__(self, msg): self.msg = msg def __str__(self): - return 'Error with argument format: '+msg + return 'Error with argument format: ' + msg ARG_STD = 0 ARG_OPT = 1 @@ -293,10 +348,15 @@ def parsecmd(line, args): else: return out -def parse((line,irc)): - line=line.split(' ') - nick=line[0].split('!')[0][1:] - chan=line[2] if line[2][0]=='#' else nick +def parse((line, irc)): + global blacklist + global msgs, msgslock + global trusted, trustedlock, gods, godslock + global doctor, die_expr + + line = line.split(' ') + nick = line[0].split('!')[0][1:] + chan = line[2] if line[2][0] == '#' else nick zwsp = '\xe2\x80\x8b' @@ -309,9 +369,9 @@ def parse((line,irc)): cmdline = [line[3][1:]] + line[4:] while '' in cmdline: cmdline.remove('') + if matchcmd(cmdline, '#echo'): text = parsecmd(cmdline, '{text}') - print text #debg irc.msg(chan, zwsp+text) elif matchcmd(cmdline, '#op'): args = parsecmd(cmdline, '{args}') @@ -344,11 +404,11 @@ def parse((line,irc)): elif matchcmd(cmdline, '#msg'): if matchcmd(cmdline, '#msg', 'nick {message}'): msgnick, message = parsecmd(cmdline, 'nick {message}') - msglock.acquire() + msgslock.acquire() if msgnick not in msgs: msgs[msgnick] = [] msgs[msgnick].append((nick, message)) - msglock.release() + msgslock.release() else: irc.msg(chan, 'Usage: #msg nick message') elif matchcmd(cmdline, '#trusted?'): @@ -423,88 +483,90 @@ def parse((line,irc)): irc.msg(chan, '%s (%s)' % (str(result), ', '.join([str(i) for i in rolls]))) else: irc.msg(chan, str(result)) - elif line[1]=='NOTICE' and line[0].split('!')[0]==':NickServ' and line[4]=='ACC': - if line[5]=='3' or line[5]=='2': + elif line[1] == 'NOTICE' and line[0].split('!')[0] == ':NickServ' and line[4] == 'ACC': + if line[5] == '3' or line[5] == '2': setauthcheckstate(line[3][1:], True) else: setauthcheckstate(line[3][1:], False) - if line[5]=='0': + if line[5] == '0': irc.msg(line[3][1:], 'Register account with NickServ') - elif line[5]=='1': - irc.msg(line[3][1:], 'PRIVMSG %s :Identify with NickServ') + elif line[5] == '1': + irc.msg(line[3][1:], 'Identify with NickServ') else: - irc.msg(line[3][1:], 'WTF, NickServ returned %s'+line[5]) - elif line[1]=='INVITE' and line[2]==irc.nick and line[3][1:] in irc.chan.split(' '): + irc.msg(line[3][1:], 'WTF, NickServ returned %s' % line[5]) + elif line[1] == 'INVITE' and line[2] == irc.nick and line[3][1:] in irc.chan.split(' '): if isauthorized(irc, nick): - irc.send('JOIN '+line[3]) - elif line[1]=='482': + irc.send('JOIN ' + line[3]) + elif line[1] == '482': irc.msg(line[3], 'Not op') - #elif line[1]=='332' or line[1]=='TOPIC': - # if line[1]=='332': - # ch=line[3] - # tp=' '.join(line[4:])[1:] - # elif line[1]=='TOPIC': - # ch=line[2] - # tp=' '.join(line[3:])[1:] - # #Do the magic here - msglock.acquire() - if (line[1]=='PRIVMSG' or line[1]=='JOIN') and nick in msgs: - for sender,msg in msgs.pop(nick): + msgslock.acquire() + if (line[1] == 'PRIVMSG' or line[1] == 'JOIN') and nick in msgs: + for sender, msg in msgs.pop(nick): irc.msg(nick, '<%s> %s' % (sender, msg)) - msglock.release() + msgslock.release() def execcmd(cmdline): - if cmdline[0]=='/q': - msglock.acquire() - f=open('msgs.txt','w') - for receiver in msgs: - for sender, msg in msgs[receiver]: - f.write('%s\t%s\t%s\n'%(receiver,sender,msg)) - f.close() - msglock.release() + if cmdline[0] == '/q': + savemessages() savetrusted() cron.ctrl('QUIT') - elif cmdline[0]=='/lt': + elif cmdline[0] == '/lt': loadtrusted() - elif cmdline[0]=='/st': + elif cmdline[0] == '/st': savetrusted() - elif cmdline[0]=='/lg': + elif cmdline[0] == '/lg': loadgods() -def help(cmd): - if cmd=='': - return '#echo #op #deop #voice #devoice #kick #src #msg #trusted? #trust #untrust #ls-trusted #invite #help' - elif cmd=='#echo': - return '#echo text echo text back' - elif cmd=='#op': - return '#op [nick] give nick or yourself op rights in case you are trusted by oonbotti2 and identified with NickServ' - elif cmd=='#deop': - return '#deop [nick] remove your/nick\'s op rights' - elif cmd=='#voice': - return '#voice [nick] give nick or yourself voice in case you are trusted by oonbotti2 and identified with NickServ' - elif cmd=='#devoice': - return '#devoice [nick] remove your or nick\'s voice in case you are trusted by oonbotti2 and identified with NickServ' - elif cmd=='#kick': - return '#kick nick reason kicks nick with specified reason' - elif cmd=='#src': - return '#src paste a link to oonbotti2\'s git repo' - elif cmd=='#msg': - return '#msg nick message send a message to nick' - elif cmd=='#trusted?': - return '#trusted? [nick] tell you if nick or yourself is trusted by oonbotti2' - elif cmd=='#trust': - return '#trust nick add nick to trusted list' - elif cmd=='#untrust': - return '#untrust nick remove nick from trusted list' - elif cmd=='#ls-trusted': - return '#ls-trusted list nicks that are trusted. use only in a query' - elif cmd=='#invite': - return '#invite nick invites nick to channel' - elif cmd=='#help': - return '#help [command] give short info of command or list commands' - elif cmd=='me': - return 'I shall.' +def usage(cmd, message = True): + usage = {'#echo': 'text', + '#op': '[nick]', + '#deop': '[nick]', + '#voice': '[nick]', + '#devoice': '[nick]', + '#kick': 'nick [reason]', + '#src': '', + '#msg': 'nick message', + '#trusted?': 'nick', + '#trust': 'nick', + '#untrust': 'nick', + '#ls-trusted': '', + '#invite': 'nick', + '#help': '[command]'} + + if cmd in usage: + if message: + return 'Usage: %s %s' % (cmd, usage[cmd]) + else: + return usage[cmd] + else: + return None + +def help(cmd): + helptext = {'#echo': '#echo text back', + '#op': 'give nick or yourself op rights in case you are trusted by oonbotti2 and identified with NickServ', + '#deop': 'remove your/nick\'s op rights', + '#voice': 'give nick or yourself voice in case you are trusted by oonbotti2 and identified with NickServ', + '#devoice': 'remove your or nick\'s voice in case you are trusted by oonbotti2 and identified with NickServ', + '#kick': 'kicks nick with specified reason', + '#src': 'paste a link to oonbotti2\'s git repo', + '#msg': 'send a message to nick', + '#trusted?': 'tell you if nick or yourself is trusted by oonbotti2', + '#trust': 'add nick to trusted list', + '#untrust': 'remove nick from trusted list', + '#ls-trusted': 'list nicks that are trusted. use only in a query', + '#invite': 'invites nick to channel', + '#help': 'give short info of command or list commands'} + + if cmd=='': + return '#echo #op #deop #voice #devoice #kick #src #msg #trusted? #trust #untrust #ls-trusted #invite #help' + elif cmd=='me': + return 'I shall.' + elif cmd in helptext: + if helptext[cmd]: + return '%s %s %s' % (cmd, usage(cmd, False), helptext[cmd]) + else: + return '%s %s' % (cmd, usage(cmd, False)) else: return None diff --git a/ircbot.py b/ircbot.py index 4168739..a8762fe 100755 --- a/ircbot.py +++ b/ircbot.py @@ -8,22 +8,27 @@ import botcmd class Channel: def __init__(self): - self.lock=threading.Lock() - self.msg=[] - def send(self,msg): + self.lock = threading.Lock() + self.msg = [] + + def send(self, msg): self.lock.acquire() self.msg.append(msg) self.lock.release() - def recv(self,wait=True): + + def recv(self, wait = True): while True: self.lock.acquire() - if len(self.msg)>0: - msg=self.msg.pop(0) + + if len(self.msg) > 0: + msg = self.msg.pop(0) self.lock.release() return msg + if not wait: self.lock.release() return None + self.lock.release() time.sleep(0.1) @@ -43,128 +48,152 @@ class Irc: self.inpc.send('PRIVMSG %s :%s' % (chan, msg)) class Connhandler(threading.Thread): - def __init__(self,server,port,chan,nick,botname,inpc,logc): + def __init__(self, server, port, chan, nick, botname, inpc, logc): threading.Thread.__init__(self) - self.server=server - self.port=port - self.nick=nick - self.name=botname - self.chan=chan - self.inpc=inpc - self.logc=logc - def send(self,s): - s=s.replace('\n','\\n').replace('\r','\\r') # Sanitize output - if len(s)>512: s=s[:512] - self.sock.send(s+'\r\n') - if s.split(' ')[0]!='PONG': - self.logc.send(s+'\n') - def check(self,line): - args=line.split(' ') - if args[0]=='PING': + + self.server = server + self.port = port + self.nick = nick + self.name = botname + self.chan = chan + self.inpc = inpc + self.logc = logc + + def send(self, s): + s = s.replace('\n', '\\n').replace('\r', '\\r') # Sanitize output + if len(s) > 512: + s = s[:512] + + self.sock.send(s + '\r\n') + if s.split(' ')[0] != 'PONG': + self.logc.send(s + '\n') + + def check(self, line): + args = line.split(' ') + if args[0] == 'PING': self.send('PONG :hjdicks') else: - self.logc.send(line+'\n') - Threadwrapper(botcmd.parse,(line,Irc(self.chan, self.nick, self.inpc))).start() + self.logc.send(line + '\n') + Threadwrapper(botcmd.parse, (line, Irc(self.chan, self.nick, self.inpc))).start() def run(self): - self.sock=None - for af, socktype, proto, canonname, sa in socket.getaddrinfo(self.server,self.port,socket.AF_UNSPEC,socket.SOCK_STREAM): + self.sock = None + for af, socktype, proto, canonname, sa in socket.getaddrinfo(self.server, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM): try: - self.sock=socket.socket(af, socktype, proto) + self.sock = socket.socket(af, socktype, proto) except socket.error: - self.sock=None + self.sock = None conntinue try: self.sock.connect(sa) except socket.error: self.sock.close() - self.sock=None + self.sock = None continue break + if self.sock is None: self.logc.send('QUIT'); sys.exit(1); + self.sock.settimeout(0.1) - self.send('NICK %s'%self.nick) - self.send('USER %s a a :%s'%(self.nick,self.name)) - f=open('startcmd.txt','r') + self.send('NICK %s' % self.nick) + self.send('USER %s a a :%s' % (self.nick, self.name)) + + f = open('startcmd.txt', 'r') for i in f: - if i[-1]=='\n': i=i[:-1] + if i[-1] == '\n': + i = i[:-1] self.send(i) f.close() - for i in self.chan.split(' '): - self.send('JOIN %s'%(i)) - buf='' + for i in self.chan.split(' '): + self.send('JOIN %s' % i) + + buf = '' while True: while True: try: - data=self.sock.recv(4096) + data = self.sock.recv(4096) break except: pass - cmd=self.inpc.recv(wait=False) - if cmd=='QUIT': - data=None + + cmd = self.inpc.recv(wait = False) + if cmd == 'QUIT': + data = None self.logc.send('QUIT') break elif cmd: self.send(cmd) + time.sleep(0.1) - if not data: break - buf+=data - buf=buf.split('\n') + + if not data: + break + + buf += data + buf = buf.split('\n') for line in buf[:-1]: - if line[-1]=='\r': line=line[:-1] + if line[-1] == '\r': + line = line[:-1] self.check(line) - buf=buf[-1] + buf = buf[-1] + self.sock.close() class Keyhandler(threading.Thread): - def __init__(self,outc): - self.outc=outc + def __init__(self, outc): + self.outc = outc threading.Thread.__init__(self) + def run(self): while True: - line=raw_input() - c=line.split(' ') - if c == '': + line = raw_input() + if line == '': continue + + c = line.split(' ') if c[0] in botcmd.concmd: botcmd.execcmd(c) - if c[0]=='/j' and len(c)==2: - self.outc.send('JOIN '+c[1]) - elif c[0]=='/m' and len(c)>2: - self.outc.send('PRIVMSG %s :%s'%(c[1],' '.join(c[2:]))) - elif c[0]=='/q' and len(c)==1: + if c[0] == '/j' and len(c) == 2: + self.outc.send('JOIN ' + c[1]) + elif c[0] == '/m' and len(c) > 2: + self.outc.send('PRIVMSG %s :%s' % (c[1], ' '.join(c[2:]))) + elif c[0] == '/q': + if len(c) > 1: + self.outc.send('QUIT :%s' % ' '.join(c[1:])) self.outc.send('QUIT') break - elif c[0][0]=='/' and c[0] not in botcmd.concmd: - self.outc.send(c[0][1:].upper()+' '+' '.join(c[1:])) + elif c[0][0] == '/' and c[0] not in botcmd.concmd: + self.outc.send(c[0][1:].upper() + ' ' + ' '.join(c[1:])) class Loghandler(threading.Thread): - def __init__(self,inpc): - self.inpc=inpc + def __init__(self, inpc): + self.inpc = inpc threading.Thread.__init__(self) + def run(self): while True: - s=self.inpc.recv() - if s=='QUIT': break + s = self.inpc.recv() + if s == 'QUIT': + break sys.stdout.write(''.join([i if ord(i)>=32 or i=='\n' else '^'+chr(ord(i)+64) for i in s])) class Threadwrapper(threading.Thread): - def __init__(self,func,arg): - self.func=func - self.arg=arg + def __init__(self, func, arg): + self.func = func + self.arg = arg threading.Thread.__init__(self) + def run(self): self.func(self.arg) if len(sys.argv)!=5: - print 'Usage: '+sys.argv[0]+' server port channel nick' + print 'Usage: ' + sys.argv[0] + ' server port channel nick' else: - keych=Channel() - logch=Channel() + keych = Channel() + logch = Channel() Keyhandler(keych).start() Loghandler(logch).start() - Connhandler(sys.argv[1],int(sys.argv[2]),sys.argv[3],sys.argv[4],sys.argv[4],keych,logch).start() + Connhandler(sys.argv[1], int(sys.argv[2]), sys.argv[3], sys.argv[4], sys.argv[4], keych, logch).start()