done moar stuff
This commit is contained in:
parent
9ca0509bb0
commit
69e20b40f5
1 changed files with 135 additions and 86 deletions
221
main.py
221
main.py
|
@ -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.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
|
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
|
||||||
for channel in CHANNELS:
|
while bot.ready and not bot.die_event.is_set() == False:
|
||||||
bot.join(channel)
|
time.sleep(1)
|
||||||
while 1:
|
|
||||||
if bot.is_alive == False: print("X")
|
if not bot.die_event.is_set():
|
||||||
time.sleep(10)
|
# set modes and join channels
|
||||||
except SystemExit:
|
bot.set_mode(MODES)
|
||||||
print("exiting...")
|
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!")
|
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 ###
|
||||||
#######################
|
#######################
|
||||||
|
|
Loading…
Reference in a new issue