2018-06-28 08:00:12 +00:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
2018-07-04 23:47:01 +00:00
|
|
|
import sys, socket, ssl, time, os, re, select
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-07-03 09:15:38 +00:00
|
|
|
sys.dont_write_bytecode = True
|
|
|
|
os.chdir(sys.path[0] or ".")
|
|
|
|
sys.path += ("..", "../IRCUFC")
|
|
|
|
|
|
|
|
import ircEvents
|
|
|
|
|
2018-06-28 08:00:12 +00:00
|
|
|
from log import Colors, Log
|
|
|
|
from mircformat import MIRCFormat
|
|
|
|
from ircReload import recompile
|
|
|
|
from ircCommands import IrcCommands
|
|
|
|
|
|
|
|
|
2018-07-03 09:15:38 +00:00
|
|
|
class IrcBot(object):
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-07-04 23:47:01 +00:00
|
|
|
def __init__(self, nickname="IRCUFC", username="McBuffer", realname="WE FIGHTIN", server="irc.underworld.no", port=9999, use_ssl=True, channel=("#IRCUFC", None), optkey="!", DEBUG=True):
|
2018-06-29 12:26:42 +00:00
|
|
|
self.sock = None
|
2018-07-04 23:47:01 +00:00
|
|
|
self.ep = None
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-06-29 12:26:42 +00:00
|
|
|
self.last_cmd = {}
|
|
|
|
self.flood_flag = {}
|
|
|
|
self.flood_count = {}
|
|
|
|
self.timeouts = {}
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-06-29 12:26:42 +00:00
|
|
|
self.mirc = MIRCFormat()
|
|
|
|
self.log = Log(DEBUG)
|
|
|
|
self.cmds = IrcCommands(self)
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-06-29 12:26:42 +00:00
|
|
|
self.server = server
|
|
|
|
self.port = port
|
2018-07-04 23:47:01 +00:00
|
|
|
self._ssl = use_ssl
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-06-29 12:26:42 +00:00
|
|
|
self.nick = nickname
|
|
|
|
self.user = username
|
|
|
|
self.real = realname
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-06-29 12:26:42 +00:00
|
|
|
self.optkey = optkey
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-06-29 12:26:42 +00:00
|
|
|
self.channel,self.chankey = channel
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-07-04 23:47:01 +00:00
|
|
|
self.recvQueue = self.sendQueue = []
|
|
|
|
|
|
|
|
self.partialLine = ''
|
2018-06-28 08:00:12 +00:00
|
|
|
|
|
|
|
def connect(self):
|
2018-07-04 23:47:01 +00:00
|
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
self.sock.setblocking(0)
|
2018-06-28 08:00:12 +00:00
|
|
|
try:
|
2018-07-04 23:47:01 +00:00
|
|
|
self.sock.connect((self.server, self.port))
|
|
|
|
except BlockingIOError:
|
|
|
|
pass
|
|
|
|
socketfd = self.sock.fileno()
|
|
|
|
self.ep = select.epoll()
|
|
|
|
self.ep.register(self.sock.fileno(), select.EPOLLIN | select.EPOLLOUT)
|
|
|
|
ready = False
|
|
|
|
while not ready:
|
|
|
|
events = self.ep.poll(1)
|
|
|
|
for _,evt in events:
|
|
|
|
if evt & select.EPOLLOUT:
|
|
|
|
ready = True
|
|
|
|
break
|
|
|
|
|
|
|
|
if self._ssl:
|
|
|
|
self.sock = ssl.wrap_socket(self.sock, do_handshake_on_connect=False)
|
|
|
|
ready = False
|
|
|
|
while not ready:
|
|
|
|
events = self.ep.poll(1)
|
|
|
|
for _,evt in events:
|
|
|
|
if evt & select.EPOLLOUT:
|
|
|
|
try:
|
|
|
|
self.sock.do_handshake()
|
|
|
|
ready = True
|
|
|
|
except ssl.SSLWantReadError:
|
|
|
|
pass
|
2018-06-28 08:00:12 +00:00
|
|
|
|
2018-07-04 23:47:01 +00:00
|
|
|
def init(self):
|
2018-06-28 08:00:12 +00:00
|
|
|
self.connect()
|
|
|
|
self.register()
|
2018-07-04 23:47:01 +00:00
|
|
|
self.run()
|
2018-06-28 08:00:12 +00:00
|
|
|
|
|
|
|
|
2018-07-04 23:47:01 +00:00
|
|
|
def run(self):
|
2018-06-28 08:00:12 +00:00
|
|
|
while True:
|
2018-07-04 23:47:01 +00:00
|
|
|
events = self.ep.poll(1)
|
|
|
|
for _,evt in events:
|
|
|
|
if evt & select.EPOLLIN:
|
|
|
|
self._recv()
|
|
|
|
elif evt & select.EPOLLOUT and len(self.sendQueue) > 0:
|
|
|
|
self._send()
|
2018-06-29 12:16:26 +00:00
|
|
|
|
2018-07-04 23:47:01 +00:00
|
|
|
self.handle_events()
|
2018-06-29 12:16:26 +00:00
|
|
|
|
2018-07-04 23:47:01 +00:00
|
|
|
def register(self):
|
|
|
|
self.queue("USER {} 0 * :{}".format(self.user, self.real))
|
|
|
|
self.queue("NICK {}".format(self.nick))
|
2018-06-29 12:16:26 +00:00
|
|
|
|
2018-07-04 23:47:01 +00:00
|
|
|
def updateNick(self):
|
|
|
|
self.queue("NICK {}".format(self.nick))
|
|
|
|
|
|
|
|
def _send(self):
|
|
|
|
while self.sendQueue:
|
|
|
|
for m in self.sendQueue:
|
|
|
|
self.log.info(">> {}".format(m.replace("\r\n", "")))
|
|
|
|
|
|
|
|
msg = self.sendQueue[0]
|
|
|
|
datasent = self.sock.send(msg.encode())
|
|
|
|
if datasent < len(msg):
|
|
|
|
self.sendQueue[0] = msg[datasent:]
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
del self.sendQueue[0]
|
|
|
|
return True
|
|
|
|
|
|
|
|
def _recv(self):
|
|
|
|
lines = []
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
newLines = self.sock.recv(512)
|
|
|
|
except BlockingIOError:
|
|
|
|
self.recvQueue = lines
|
2018-06-29 12:16:26 +00:00
|
|
|
return
|
2018-07-04 23:47:01 +00:00
|
|
|
except ssl.SSLWantReadError:
|
|
|
|
self.recvQueue = lines
|
|
|
|
return
|
|
|
|
if not newLines:
|
|
|
|
lines += [None]
|
|
|
|
break
|
|
|
|
elif len(newLines) == 0:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
newLines = str(newLines, 'utf-8', 'ignore')
|
|
|
|
if newLines[-2:] == "\r\n":
|
|
|
|
msgs = (self.partialLine + newLines).split("\r\n")[:-1]
|
|
|
|
self.partialLine = ""
|
|
|
|
else:
|
|
|
|
msgs = (self.partialLine + newLines).split("\r\n")
|
|
|
|
if len(msgs) > 1:
|
|
|
|
self.partialLine = msgs[-1]
|
|
|
|
msgs = msgs[:-1]
|
|
|
|
lines += msgs
|
|
|
|
|
|
|
|
self.recvQueue = lines
|
|
|
|
for m in self.recvQueue():
|
|
|
|
self.log.info("<< {}".format(m))
|
|
|
|
|
|
|
|
def handle_events(self):
|
|
|
|
while self.recvQueue:
|
|
|
|
data = self.recvQueue[0]
|
|
|
|
self.log.info("<< {}".format(data))
|
|
|
|
line = data.split(" ")
|
|
|
|
if line[0][1:] == 'ING':
|
|
|
|
ircEvents.eventPING(self, line)
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
if hasattr(ircEvents, "event{}".format(line[1].upper())):
|
|
|
|
getattr(ircEvents, "event{}".format(line[1].upper()))(self, line)
|
|
|
|
except Exception as e:
|
|
|
|
self.log.error("Error in getattr()", e)
|
|
|
|
|
|
|
|
del self.recvQueue[0]
|
2018-06-28 08:00:12 +00:00
|
|
|
|
|
|
|
def join(self):
|
2018-07-04 23:47:01 +00:00
|
|
|
self.queue("JOIN {} {}".format(self.channel, self.chankey)) if self.chankey else self.queue("JOIN {}".format(self.channel))
|
2018-06-28 08:00:12 +00:00
|
|
|
|
|
|
|
|
2018-07-04 23:47:01 +00:00
|
|
|
def queue(self, msg):
|
2018-06-28 08:00:12 +00:00
|
|
|
msg = msg.replace("\r", "")
|
|
|
|
msg = msg.replace("\n", "")
|
2018-07-04 23:47:01 +00:00
|
|
|
if len(msg) > (512 - len("\r\n")):
|
|
|
|
for i in range(0, len(msg), 512 - len("\r\n")):
|
|
|
|
m = msg[i:512 - len("\r\n")]
|
|
|
|
m = m + "\r\n"
|
|
|
|
self.sendQueue += [m]
|
|
|
|
else:
|
|
|
|
msg = msg + "\r\n"
|
|
|
|
self.sendQueue += [msg]
|
2018-06-28 08:00:12 +00:00
|
|
|
|
|
|
|
def isAdmin(self, ident):
|
|
|
|
ret = False
|
2018-07-03 09:15:38 +00:00
|
|
|
for line in [line.strip() for line in open('assets/admins', 'r').readlines() if line]:
|
2018-06-28 08:00:12 +00:00
|
|
|
if re.compile(line.replace('*', '.*')).search(ident):
|
|
|
|
ret = True
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
def handle_msg(self, chan, admin, nick, user, host, msg):
|
2018-07-04 23:47:01 +00:00
|
|
|
if len(msg) == 0:
|
|
|
|
return
|
2018-06-28 08:00:12 +00:00
|
|
|
args = msg.split()
|
|
|
|
if admin:
|
|
|
|
if args[0] == '{}reload'.format(self.optkey):
|
|
|
|
ret = recompile(args[1]) # Let you add new code to the bot without restarting it
|
|
|
|
if ret == True:
|
2018-06-28 10:24:40 +00:00
|
|
|
self.privmsg(chan, "{} recompiled successfully!".format(self.mirc.color(args[1], self.mirc.colors.GREEN)))
|
2018-06-28 08:00:12 +00:00
|
|
|
return
|
|
|
|
else:
|
2018-06-28 10:24:40 +00:00
|
|
|
self.privmsg(chan, "Man we had a issue while recompiling {}".format(self.mirc.color(args[1], self.mirc.colors.GREEN)))
|
2018-06-28 08:00:12 +00:00
|
|
|
self.log.error(ret)
|
|
|
|
return
|
|
|
|
|
|
|
|
self.cmds.handle_msg(chan, admin, nick, user, host, msg)
|
|
|
|
|
|
|
|
|
|
|
|
def privmsg(self, chan, msg):
|
|
|
|
if chan not in self.timeouts:
|
|
|
|
self.timeouts[chan] = {'last_cmd': time.time(), 'burst': 0, 'timeout': 0}
|
2018-07-04 23:47:01 +00:00
|
|
|
self.queue("PRIVMSG {} :{}".format(chan, msg))
|
|
|
|
time.sleep(self.timeouts[chan]['timeout'])
|
2018-06-28 10:24:40 +00:00
|
|
|
self.editTimeouts(chan)
|
2018-06-28 08:00:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-06-28 10:24:40 +00:00
|
|
|
def editTimeouts(self, chan):
|
2018-06-28 08:00:12 +00:00
|
|
|
if (time.time() - self.timeouts[chan]['last_cmd']) < 3:
|
|
|
|
self.timeouts[chan]['burst'] += 1
|
|
|
|
else:
|
|
|
|
self.timeouts[chan]['burst'] = 0
|
|
|
|
|
|
|
|
if self.timeouts[chan]['burst'] > 3:
|
|
|
|
self.timeouts[chan]['timeout'] += 0.075
|
|
|
|
else:
|
|
|
|
self.timeouts[chan]['timeout'] = 0
|
|
|
|
|
|
|
|
if self.timeouts[chan]['timeout'] > 0.4:
|
|
|
|
self.timeouts[chan]['timeout'] = 0.4
|
|
|
|
|
|
|
|
self.timeouts[chan]['last_cmd'] = time.time()
|
|
|
|
|
|
|
|
|
|
|
|
def action(self, chan, msg):
|
|
|
|
self.privmsg(chan, "\x01ACTION {}\x01".format(msg))
|