PHP Code:
#!/usr/bin/env python3
import sys
import socket
import select
import re
import random
from time import time
from subprocess import Popen, PIPE
from optparse import OptionParser
msgexp = re.compile(r'(PING )?:(([^! ]+)(!([^@]+)@(\S+))?)( ([^:]+) :(.*))?')
#\1 = PING
#\3 = nick / server
#\5 = user
#\6 = host
#\8 = command args
#\9 = text
def runcmd(*args):
return Popen([str(i) for i in args], stdout=PIPE).communicate()[0]
class Connection:
def __init__(self, options):
self.host = options.host
self.port = int(options.port)
self.channel = options.channel
self.nick = options.nick
self.ident = options.ident
self.rname = options.rname
self.buffer = ''
self.sock = socket.socket()
self.sock.connect((self.host, self.port))
def send(self, text):
print('<<', text)
# encode utf-8 to binary, only necessary in python3
self.sock.send(text.encode())
def getline(self):
# use a buffer so we always get a complete line and not
# things like:
# >> ... :adams.freenode.net 376 reallysimplebot :End of /MOTD com
# >> mand.
n = self.buffer.find('\r\n')
while n == -1:
got = self.sock.recv(1024).decode()
if got == '':
line = self.buffer
self.buffer = ''
return line
self.buffer += got
n = self.buffer.find('\r\n')
line = self.buffer[:n]
self.buffer = self.buffer[n + 2:]
print('>>', line)
return line
def data_available(self, timeout=0):
if timeout < 0:
timeout = 0
if len(self.buffer) > 0 or\
self.sock.fileno() in \
select.select([self.sock.fileno()], [], [], timeout)[0]:
return True
return False
def init(self):
self.sendnick()
self.senduser()
while True:
line = self.getline()
m = msgexp.match(line)
if m is None:
print("Error, expression didn't match")
if m.group(1):
self.sendpong()
continue
elif m.group(9) is not None:
if 'VERSION' in m.group(9):
self.send('VERSION')
elif 'End of /MOTD command' in m.group(9) or \
'376' in m.group(8):
break
self.sendjoin()
def sendnick(self, nick=None):
if nick is None:
nick = self.nick
else:
self.nick = nick
self.send('NICK {0}\r\n'.format(nick))
def senduser(self, ident=None, rname=None):
if ident is None:
ident = self.ident
else:
self.ident = ident
if rname is None:
rname = self.rname
else:
self.rname = rname
self.send('USER {0} 0 * :{1}\r\n'.format(ident, rname))
def sendjoin(self, channel=None):
if channel is None:
channel = self.channel
else:
self.channel = channel
self.send('JOIN {0}\r\n'.format(channel))
def sendmsg(self, text, recipient=None):
if recipient is None:
recipient = self.channel
self.send('PRIVMSG {0} :{1}\r\n'.format(recipient, text))
def sendpong(self):
self.send('PONG {0}\r\n'.format(self.channel))
def sendquit(self, msg='Leaving'):
self.send('QUIT :{0}\r\n'.format(msg))
def __iter__(self):
while True:
line = self.getline()
if len(line) == 0:
break
yield line
class IRCbot:
def __init__(self, options):
self.options = options
self.nick = options.nick
self.owner = options.owner
self.replydict = {}
self.channelmsg = 'PRIVMSG ' + options.channel # for now, ignore private messages
self.conn = Connection(options)
self.conn.init()
self.time = time()
def talking_to_me(self, text, textexp):
return re.match(r'^{0}[,:]? {1}$'.format(self.nick, textexp),
text)
def reply(self, nick, text):
if self.talking_to_me(text, r'creator\?') is not None:
return 'I was created by Ziekfiguur'
# this part is inspired by Bucket -- http://wiki.xkcd.com/irc/Bucket
m = self.talking_to_me(text, r'(.*?) <([^>]+)> (.*)')
if m is not None and (self.owner is None or self.owner == nick):
if m.group(2) == 'action':
self.replydict[m.group(1)] = '\x01ACTION ' + m.group(3) + '\x01'
elif m.group(2) == 'reply':
self.replydict[m.group(1)] = '{name}, ' + m.group(3)
else:
self.replydict[m.group(1)] = ' '.join(m.groups())
# this part is inspired by flyingferret, also from #xkcd, can't find the docs
m = self.talking_to_me(text, r'((.*) or ([^?]+))\??')
if m is not None:
print('choose {' + ', '.join(m.group(1).split(' or ')) + '}')
return nick + ', ' + random.choice(m.group(1).split(' or '))
if text.strip() in self.replydict:
return self.replydict[text.strip()].format(name=nick)
def random(self):
something = runcmd('fortune', '-sn128', 'science') # replace this if you dont have the fortune command installed
something = ' '.join(something.decode().split()) # remove unnecessary whitespace, also from in between words
self.conn.sendmsg(something)
def mainloop(self):
while True:
if self.conn.data_available(self.time + 300 - time()):
line = self.conn.getline()
m = msgexp.match(line)
if m is None:
print("Error, expression didn't match")
continue
if m.group(1):
self.conn.sendpong()
continue
nick = m.group(3)
if nick == self.nick: # ignore yourself
continue
command = m.group(8)
text = m.group(9)
if command == 'JOIN':
self.conn.sendmsg('Welcome {0}!'.format(nick))
self.time = time()
elif command == self.channelmsg:
if self.talking_to_me(text, '[Dd]ie!') is not None \
and (self.owner is None or self.owner == nick):
break
r = self.reply(nick, text)
if r is not None:
self.conn.sendmsg(r)
self.time = time()
if time() > self.time + 300: # if noone joined or said anything in 5 minutes
self.random()
self.time = time()
self.conn.sendquit()
def main(argv):
parser = OptionParser()
parser.add_option('-H', '--host', dest='host',
help='Connect to HOST, default is chat.freenode.net',
metavar='HOST', default='chat.freenode.net')
parser.add_option('-p', '--port', dest='port',
help='Connect to PORT, default is 6667',
metavar='HOST', default='6667')
parser.add_option('-c', '--channel', dest='channel',
help='Join CHANNEL, default is #ufpc',
metavar='CHANNEL', default='#ufpc')
parser.add_option('-n', '--nick', dest='nick',
help='use NICK, default is botz0r',
metavar='NICK', default='botz0r')
parser.add_option('-i', '--ident', dest='ident',
help='use IDENT, default is guest',
metavar='IDENT', default='guest')
parser.add_option('-r', '--rname', dest='rname',
help='use RNAME, default is nobody',
metavar='RNAME', default='nobody')
parser.add_option('-o', '--owner', dest='owner',
help='Only listen to commands from OWNER, if this is not '
'set, commands will be taken from everyone',
metavar='OWNER', default=None)
(options, args) = parser.parse_args(argv)
try:
int(options.port)
except ValueError:
print('Error, port has to be a number')
return
bot = IRCbot(options)
bot.mainloop()
if __name__ == '__main__':
main(sys.argv)
Bookmarks