1
0
Fork 0

done moar stuff

This commit is contained in:
fanir 2014-03-01 01:25:12 +01:00
parent 9ca0509bb0
commit 69e20b40f5

219
main.py
View file

@ -26,13 +26,16 @@
# SETTINGS CAN BE FOUND AT THE BOTTOM OF THE FILE # SETTINGS CAN BE FOUND AT THE BOTTOM OF THE FILE
import sys, time, string, socket, re, signal import sys, time, string, socket, re, signal, struct
from select import poll, POLLIN, POLLPRI, POLLOUT,\ from select import poll, POLLIN, POLLPRI, POLLOUT,\
POLLERR, POLLHUP, POLLNVAL POLLERR, POLLHUP, POLLNVAL
from threading import Thread#, Event from threading import Thread, Event
from APIcollection import Twitter
bot = None bot = None
class log(): class log():
DEBUG, INFO, WARNING, ERROR, FATAL, SILENT =\ DEBUG, INFO, WARNING, ERROR, FATAL, SILENT =\
0, 1, 2, 3, 4, 5 0, 1, 2, 3, 4, 5
@ -44,6 +47,7 @@ class log():
raise ValueError("That's not a loglevel!") raise ValueError("That's not a loglevel!")
class pircbot(): class pircbot():
def encode(self, textstring): def encode(self, textstring):
for codec in self.encodings: for codec in self.encodings:
@ -59,68 +63,53 @@ class pircbot():
def __init__(self, nicknames, server, port=6667, ident="pircbot", def __init__(self, nicknames, server, port=6667, ident="pircbot",
realname="pircbot", encodings=("utf-8", "latin-1"), realname="pircbot", encodings=("utf-8", "latin-1"),
command_prefix=".", admins=""): command_prefix=".", admins="", serverpasswd="", parser_wait_time=0.1):
self.nicknames = nicknames
self.server = server self.server = server
self.port = port self.port = port
self.serverpasswd = serverpasswd
self.encodings = encodings
self.nicknames = nicknames
self.ident = ident self.ident = ident
self.realname = realname self.realname = realname
self.encodings = encodings
self.admins = admins self.admins = admins
self.cmdprefix = command_prefix self.cmdprefix = command_prefix
self.parser_wait_time=parser_wait_time
self.user = {}
self.socket = None self.socket = None
self.recvloop = None self.recvloop = None
self.recvbuffer = bytearray(1024) self.recvbuffer = bytearray(1024)
self.run = False
self.ready = False
self.is_alive = False
self.need_nick = True
def recv(self): self.ready = False
self.die_event = Event()
def recv_loop(self):
""" """
Loop for reciving data Loop for reciving data
""" """
parser = Thread(target=self.parse, name="parser") parser = Thread(target=self.parse_loop, name="parser")
p = poll() p = poll()
p.register(self.socket.fileno(), POLLIN) p.register(self.socket.fileno(), POLLIN)
while self.run: while not self.die_event.is_set():
ap = p.poll(1000) ap = p.poll(1000)
if (self.socket.fileno(), POLLIN) in ap: if (self.socket.fileno(), POLLIN) in ap:
self.recvbuffer.extend(self.socket.recv(1024)) self.recvbuffer.extend(self.socket.recv(1024))
if not parser.is_alive(): if not parser.is_alive():
parser = Thread(target=self.parse, name="parser") parser = Thread(target=self.parse_loop, name="parser")
parser.start() parser.start()
if parser.is_alive(): parser.join() print("--- RECVLOOP EXITING ---")
def connect(self): def parse_loop(self):
# connect
self.socket = socket.socket()
try:
self.socket.connect((self.server, self.port))
except socket.error as e:
log.show(log.FATAL, "Fehler: %s" % e)
return
self.run = True
# start getting data
self.recvloop = Thread(target=self.recv, name="recvloop")
self.recvloop.start()
# get a nick
self.socket.send(self.encode("NICK %s\r\n" % self.nicknames.pop(0)))
self.socket.send(self.encode("USER %s %s bla :%s\r\n" % (self.ident, self.server, self.realname)))
self.is_alive = True
def parse(self):
""" """
Loop for parsing incoming data Loop for parsing incoming data
""" """
#line = "" while not self.die_event.is_set():# and self.recvbuffer.endswith(b"\r\n"):# != b"":
origin = {} if self.recvbuffer.endswith(b"\r\n"):
while self.run and self.recvbuffer != b"":
# get and decode line from buffer # get and decode line from buffer
rawline, _, self.recvbuffer = self.recvbuffer.partition(b"\r\n") rawline, _, self.recvbuffer = self.recvbuffer.partition(b"\r\n")
rawline = self.decode(rawline) rawline = self.decode(rawline)
@ -129,9 +118,9 @@ class pircbot():
head = line[0].lstrip("\x00").split(" ") head = line[0].lstrip("\x00").split(" ")
larg = line[1] if len(line)>1 else "" larg = line[1] if len(line)>1 else ""
# parse prefix # parse prefix
origin = {}
if head[0].startswith(":"): if head[0].startswith(":"):
prefix = head.pop(0)[1:] prefix = head.pop(0)[1:]
origin.clear()
if "@" in prefix: if "@" in prefix:
origin["nick"], origin["ident"], origin["host"] = re.match(r"(\S+)!(\S+)@(\S+)", prefix).groups() origin["nick"], origin["ident"], origin["host"] = re.match(r"(\S+)!(\S+)@(\S+)", prefix).groups()
else: else:
@ -144,24 +133,60 @@ class pircbot():
params = head params = head
params.append(larg) params.append(larg)
log.show(log.DEBUG, ">> %s" % rawline) log.show(log.DEBUG, " > %s" % rawline)
# PING
if command == "PING": if command == "PING":
self.socket.send(self.encode("PONG %s\r\n" % params[0])) self.send("PONG %s" % params[0])
# PRIVMSG
elif command == "PRIVMSG": elif command == "PRIVMSG":
if params[1].startswith(self.cmdprefix): if params[1].startswith(self.cmdprefix):
args = params[1].lstrip(self.cmdprefix).split(" ") args = params[1].lstrip(self.cmdprefix).split(" ")
sndline = self.on_command(prefix, origin, params[0], args[0], args[1:]) sndline = self.on_command(prefix, origin, params[0], args[0], args[1:])
if sndline != None: if sndline != None:
log.show(log.DEBUG, "<< %s" % sndline) self.send(sndline)
self.socket.send(self.encode(sndline)) # 001 (RPL_WELCOME)
elif command == "001": elif command == "001":
self.user["mask"] = re.search(r" (\S+!\S+@\S+)$", rawline.split(" :", 1)[1]).groups()[0]
self.user["nick"], self.user["ident"], self.user["host"] = re.match(r"(\S+)!(\S+)@(\S+)", self.user["mask"]).groups()
self.ready = True self.ready = True
# 433 (ERR_NICKNAMEINUSE)
elif command == "433": elif command == "433":
self.socket.send(self.encode("NICK %s\r\n" % self.nicknames.pop(0))) self.send("NICK %s" % self.nicknames.pop(0))
# KILL
elif command == "KILL": elif command == "KILL":
print("Got killed: %s", rawline) log.show(log.WARNING, "Got killed by %s: %s" % (params[0], params[1:]))
self.die() self.quit(send=False)
else:
time.sleep(self.parser_wait_time)
print("--- PARSELOOP EXITING ---")
def send(self, data):
log.show(log.DEBUG, "< %s" % data)
try: self.socket.send(self.encode("".join((data, "\r\n"))))
except BrokenPipeError as e:
log.show(log.FATAL, e)
self.quit(send=False)
def connect(self):
# connect
self.socket = socket.socket()
try:
self.socket.connect((self.server, self.port))
except socket.error as e:
log.show(log.FATAL, "Fehler: %s" % e)
return
# start getting data
self.recvloop = Thread(target=self.recv_loop, name="recvloop")
self.recvloop.start()
# optionally send a server password
if self.serverpasswd != "": self.send("PASS %s" % self.serverpasswd)
# get a nick
self.send("NICK %s" % self.nicknames.pop(0))
# set user data
self.send("USER %s 0 * :%s" % (self.ident, self.realname))
def is_admin(self, user): def is_admin(self, user):
for admin in self.admins: for admin in self.admins:
@ -169,24 +194,29 @@ class pircbot():
return True return True
return False return False
def die(self): def quit(self, reason="", send=True):
self.run = False if send:
self.recvloop.join() try:
self.is_alive = False self.send("QUIT :%s" % reason)
sys.exit() except: pass
self.die_event.set()
def quit(self, reason=""): print("--- SOCKET CLOSING ---")
self.socket.send(self.encode("QUIT :%s\n" % reason)) try:
self.run = False self.socket.shutdown(socket.SHUT_RDWR)
self.recvloop.join() self.socket.close()
self.is_alive = False except: pass
def join(self, channel): def join(self, channel):
print(channel) while not self.ready: time.sleep(1)
self.socket.send(self.encode("JOIN %s\n" % channel)) self.send("JOIN %s" % channel)
def part(self, channel): def part(self, channel):
self.socket.send(self.encode("PART %s\n" % channel)) while not self.ready: time.sleep(1)
self.send("PART %s" % channel)
def set_mode(self, modes):
while not self.ready: time.sleep(1)
self.send("MODE %s :%s" % (self.user["nick"], modes))
def on_command(self, prefix, origin, source, command, params): def on_command(self, prefix, origin, source, command, params):
""" """
@ -199,19 +229,26 @@ class pircbot():
Command is the command for the bot. Command is the command for the bot.
Params contains a list of originally space separated parameters. Params contains a list of originally space separated parameters.
""" """
print(params) # hello
if command.startswith("hello"): if command == "hello":
greeting = "".join(("Hi " + origin["nick"] +"!")) greeting = "".join(("Hi " + origin["nick"] +"!"))
if source[0] in {"#", "+", "!", "&"}: if source[0] in {"#", "+", "!", "&"}:
return "PRIVMSG %s :%s\r\n" % (source, greeting) return "PRIVMSG %s :%s" % (source, greeting)
else: else:
return "PRIVMSG %s :%s\r\n" % (origin["nick"], greeting) return "PRIVMSG %s :%s" % (origin["nick"], greeting)
elif command.startswith("join") and len(params)>0: # join <channel>
elif command == "join" and len(params)>0:
if self.is_admin(origin["nick"]): self.join(params[0]) if self.is_admin(origin["nick"]): self.join(params[0])
else: return "PRIVMSG %s :You can't do that!\r\n" % origin["nick"] else: return "PRIVMSG %s :You can't do that!" % origin["nick"]
elif command.startswith("part") and len(params)>0: # part <channel>
elif command == "part" and len(params)>0:
if self.is_admin(origin["nick"]): self.part(params[0]) if self.is_admin(origin["nick"]): self.part(params[0])
else: return "PRIVMSG %s :You can't do that!\r\n" % origin["nick"] else: return "PRIVMSG %s :You can't do that!" % origin["nick"]
# die [<quitmsg>]
elif command == "die":
if self.is_admin(origin["nick"]):
self.quit("".join(params) if len(params)>0 else "".join((origin["nick"], " shot me, dying now... Bye...")))
def parseargs(): def parseargs():
@ -222,31 +259,34 @@ def parseargs():
#p.add_argument("--daemon", "-d", type = bool, choices = [1, 0], default=1, help="Daemonize, Default: 1") #p.add_argument("--daemon", "-d", type = bool, choices = [1, 0], default=1, help="Daemonize, Default: 1")
return p.parse_args() return p.parse_args()
def KeyboardInterruptHandler(signum, frame):
global bot
print("Got Ctrl-C, dying now...")
bot.quit("")
sys.exit()
def main(): def main():
global bot global bot
signal.signal(signal.SIGINT, KeyboardInterruptHandler)
args = parseargs() args = parseargs()
if args.action == "start": if args.action == "start":
bot = pircbot(nicknames=NICKNAMES, ident=IDENT, realname=REALNAME, bot = pircbot(nicknames=NICKNAMES, ident=IDENT, realname=REALNAME,
server=SERVER, port=PORT, encodings=ENCODINGS, command_prefix=COMMAND_PREFIX, admins=ADMINS) server=SERVER, port=PORT, encodings=ENCODINGS, command_prefix=COMMAND_PREFIX,
admins=ADMINS, serverpasswd=SERVERPASSWD, parser_wait_time=PARSER_WAIT_TIME)
try: try:
bot.connect() bot.connect()
while bot.ready == False: time.sleep(1) # wait for the bot to become ready
while bot.ready and not bot.die_event.is_set() == False:
time.sleep(1)
if not bot.die_event.is_set():
# set modes and join channels
bot.set_mode(MODES)
for channel in CHANNELS: for channel in CHANNELS:
bot.join(channel) bot.join(channel)
while 1:
if bot.is_alive == False: print("X") # while bot is active, do nothing
time.sleep(10) while not bot.die_event.is_set():
except SystemExit: time.sleep(1)
print("exiting...") except KeyboardInterrupt:
log.show(log.INFO, "Got Ctrl-C, dying now...")
bot.quit()
elif args.action == "stop": print("nope!") elif args.action == "stop": print("nope!")
print("--- MAIN EXITING ---")
return 0 return 0
@ -287,6 +327,8 @@ if __name__ == '__main__':
# supported. Maybe in a future, far far away... # supported. Maybe in a future, far far away...
PORT = 6667 PORT = 6667
SERVERPASSWD = ""
# A comma-seperated list of channels to join, enclosed by braces. # A comma-seperated list of channels to join, enclosed by braces.
CHANNELS = ["#bots", "#main"] CHANNELS = ["#bots", "#main"]
@ -303,6 +345,13 @@ if __name__ == '__main__':
COMMAND_PREFIX = "." COMMAND_PREFIX = "."
# The time the parser for incoming data should wait between each attempt to read new data in seconds.
# High values will certainly make the bot reply slowly while very low values increads cpu load and therefore will perform badly on slow machines.
# You should keep it between 1 and 0.001 seconds.
# For gods sake, don't set it to 0!
PARSER_WAIT_TIME = 0.05
####################### #######################
### END OF SETTINGS ### ### END OF SETTINGS ###
####################### #######################