From 69e20b40f5890fabeb2ac427940e005dca6d20f6 Mon Sep 17 00:00:00 2001 From: Fanir Date: Sat, 1 Mar 2014 01:25:12 +0100 Subject: [PATCH] done moar stuff --- main.py | 221 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 135 insertions(+), 86 deletions(-) diff --git a/main.py b/main.py index 1ad0b96..bae43c5 100755 --- a/main.py +++ b/main.py @@ -26,13 +26,16 @@ # 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,\ POLLERR, POLLHUP, POLLNVAL -from threading import Thread#, Event +from threading import Thread, Event + +from APIcollection import Twitter bot = None + class log(): DEBUG, INFO, WARNING, ERROR, FATAL, SILENT =\ 0, 1, 2, 3, 4, 5 @@ -44,6 +47,7 @@ class log(): raise ValueError("That's not a loglevel!") + class pircbot(): def encode(self, textstring): for codec in self.encodings: @@ -59,68 +63,53 @@ class pircbot(): def __init__(self, nicknames, server, port=6667, ident="pircbot", realname="pircbot", encodings=("utf-8", "latin-1"), - command_prefix=".", admins=""): - self.nicknames = nicknames + command_prefix=".", admins="", serverpasswd="", parser_wait_time=0.1): self.server = server self.port = port + self.serverpasswd = serverpasswd + self.encodings = encodings + + self.nicknames = nicknames self.ident = ident self.realname = realname - self.encodings = encodings + self.admins = admins self.cmdprefix = command_prefix + + self.parser_wait_time=parser_wait_time + + self.user = {} + self.socket = None self.recvloop = None self.recvbuffer = bytearray(1024) - self.run = False + self.ready = False - self.is_alive = False - self.need_nick = True + + self.die_event = Event() - def recv(self): + def recv_loop(self): """ Loop for reciving data """ - parser = Thread(target=self.parse, name="parser") + parser = Thread(target=self.parse_loop, name="parser") p = poll() p.register(self.socket.fileno(), POLLIN) - while self.run: + while not self.die_event.is_set(): ap = p.poll(1000) if (self.socket.fileno(), POLLIN) in ap: self.recvbuffer.extend(self.socket.recv(1024)) if not parser.is_alive(): - parser = Thread(target=self.parse, name="parser") + parser = Thread(target=self.parse_loop, name="parser") parser.start() - if parser.is_alive(): parser.join() + print("--- RECVLOOP EXITING ---") - 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 - - 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): + def parse_loop(self): """ Loop for parsing incoming data """ - #line = "" - origin = {} - while self.run and self.recvbuffer != b"": + while not self.die_event.is_set():# and self.recvbuffer.endswith(b"\r\n"):# != b"": + if self.recvbuffer.endswith(b"\r\n"): # get and decode line from buffer rawline, _, self.recvbuffer = self.recvbuffer.partition(b"\r\n") rawline = self.decode(rawline) @@ -129,9 +118,9 @@ class pircbot(): head = line[0].lstrip("\x00").split(" ") larg = line[1] if len(line)>1 else "" # parse prefix + origin = {} if head[0].startswith(":"): prefix = head.pop(0)[1:] - origin.clear() if "@" in prefix: origin["nick"], origin["ident"], origin["host"] = re.match(r"(\S+)!(\S+)@(\S+)", prefix).groups() else: @@ -144,24 +133,60 @@ class pircbot(): params = head params.append(larg) - log.show(log.DEBUG, ">> %s" % rawline) + log.show(log.DEBUG, " > %s" % rawline) + # 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": if params[1].startswith(self.cmdprefix): args = params[1].lstrip(self.cmdprefix).split(" ") sndline = self.on_command(prefix, origin, params[0], args[0], args[1:]) if sndline != None: - log.show(log.DEBUG, "<< %s" % sndline) - self.socket.send(self.encode(sndline)) + self.send(sndline) + # 001 (RPL_WELCOME) 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 + # 433 (ERR_NICKNAMEINUSE) 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": - print("Got killed: %s", rawline) - self.die() + log.show(log.WARNING, "Got killed by %s: %s" % (params[0], params[1:])) + 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): for admin in self.admins: @@ -169,24 +194,29 @@ class pircbot(): return True return False - def die(self): - self.run = False - self.recvloop.join() - self.is_alive = False - sys.exit() - - def quit(self, reason=""): - self.socket.send(self.encode("QUIT :%s\n" % reason)) - self.run = False - self.recvloop.join() - self.is_alive = False + def quit(self, reason="", send=True): + if send: + try: + self.send("QUIT :%s" % reason) + except: pass + self.die_event.set() + print("--- SOCKET CLOSING ---") + try: + self.socket.shutdown(socket.SHUT_RDWR) + self.socket.close() + except: pass def join(self, channel): - print(channel) - self.socket.send(self.encode("JOIN %s\n" % channel)) + while not self.ready: time.sleep(1) + self.send("JOIN %s" % 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): """ @@ -199,19 +229,26 @@ class pircbot(): Command is the command for the bot. Params contains a list of originally space separated parameters. """ - print(params) - if command.startswith("hello"): + # hello + if command == "hello": greeting = "".join(("Hi " + origin["nick"] +"!")) if source[0] in {"#", "+", "!", "&"}: - return "PRIVMSG %s :%s\r\n" % (source, greeting) + return "PRIVMSG %s :%s" % (source, greeting) else: - return "PRIVMSG %s :%s\r\n" % (origin["nick"], greeting) - elif command.startswith("join") and len(params)>0: + return "PRIVMSG %s :%s" % (origin["nick"], greeting) + # join + elif command == "join" and len(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"] - elif command.startswith("part") and len(params)>0: + else: return "PRIVMSG %s :You can't do that!" % origin["nick"] + # part + elif command == "part" and len(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 [] + 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(): @@ -222,31 +259,34 @@ def parseargs(): #p.add_argument("--daemon", "-d", type = bool, choices = [1, 0], default=1, help="Daemonize, Default: 1") return p.parse_args() -def KeyboardInterruptHandler(signum, frame): - global bot - print("Got Ctrl-C, dying now...") - bot.quit("") - sys.exit() - def main(): global bot - signal.signal(signal.SIGINT, KeyboardInterruptHandler) - args = parseargs() if args.action == "start": 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: bot.connect() - while bot.ready == False: time.sleep(1) - for channel in CHANNELS: - bot.join(channel) - while 1: - if bot.is_alive == False: print("X") - time.sleep(10) - except SystemExit: - print("exiting...") + # 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: + bot.join(channel) + + # while bot is active, do nothing + while not bot.die_event.is_set(): + time.sleep(1) + except KeyboardInterrupt: + log.show(log.INFO, "Got Ctrl-C, dying now...") + bot.quit() + elif args.action == "stop": print("nope!") + print("--- MAIN EXITING ---") return 0 @@ -287,6 +327,8 @@ if __name__ == '__main__': # supported. Maybe in a future, far far away... PORT = 6667 + SERVERPASSWD = "" + # A comma-seperated list of channels to join, enclosed by braces. CHANNELS = ["#bots", "#main"] @@ -303,6 +345,13 @@ if __name__ == '__main__': 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 ### #######################