aboutsummaryrefslogtreecommitdiff
path: root/src/IRCBot.py
diff options
context:
space:
mode:
authorGravatar jesopo2018-09-24 15:13:27 +0100
committerGravatar jesopo2018-09-24 15:13:27 +0100
commitecb9d7cb3f4435457560e03201bbed57a469d548 (patch)
tree5a010f97c209558cdd2d40327d41e6806aedde94 /src/IRCBot.py
parentRemove empty spaces in coins.py (diff)
signature
Move most code in root directory to src/
Diffstat (limited to 'src/IRCBot.py')
-rw-r--r--src/IRCBot.py197
1 files changed, 197 insertions, 0 deletions
diff --git a/src/IRCBot.py b/src/IRCBot.py
new file mode 100644
index 00000000..b94d31d5
--- /dev/null
+++ b/src/IRCBot.py
@@ -0,0 +1,197 @@
+import os, select, sys, threading, time, traceback, uuid
+from . import EventManager, Exports, IRCLineHandler, IRCServer, Logging
+from . import ModuleManager, Timer
+
+class Bot(object):
+ def __init__(self):
+ self.start_time = time.time()
+ self.lock = threading.Lock()
+ self.args = None
+ self.database = None
+ self.config = None
+ self.bot_directory = os.path.dirname(os.path.realpath(__file__))
+ self.servers = {}
+ self.running = True
+ self.poll = select.epoll()
+ self.timers = []
+
+ self._events = None
+ self._exports = None
+ self.modules = None
+ self.log = None
+ self.line_handler = None
+
+ def add_server(self, server_id, connect=True):
+ (_, alias, hostname, port, password, ipv4, tls, nickname,
+ username, realname) = self.database.servers.get(server_id)
+
+ new_server = IRCServer.Server(self, self._events, server_id, alias,
+ hostname, port, password, ipv4, tls, nickname, username,
+ realname)
+ if not new_server.get_setting("connect", True):
+ return
+ self._events.on("new.server").call(server=new_server)
+ if connect and new_server.get_setting("connect", True):
+ self.connect(new_server)
+ return new_server
+ def connect(self, server):
+ try:
+ server.connect()
+ except:
+ sys.stderr.write("Failed to connect to %s\n" % str(server))
+ traceback.print_exc()
+ return False
+ self.servers[server.fileno()] = server
+ self.poll.register(server.fileno(), select.EPOLLOUT)
+ return True
+ def setup_timers(self, event):
+ for setting, value in self.find_settings("timer-%"):
+ id = setting.split("timer-", 1)[1]
+ self.add_timer(value["event-name"], value["delay"], value[
+ "next-due"], id, **value["kwargs"])
+ def timer_setting(self, timer):
+ self.set_setting("timer-%s" % timer.id, {
+ "event-name": timer.event_name, "delay": timer.delay,
+ "next-due": timer.next_due, "kwargs": timer.kwargs})
+ def timer_setting_remove(self, timer):
+ self.timers.remove(timer)
+ self.del_setting("timer-%s" % timer.id)
+ def add_timer(self, event_name, delay, next_due=None, id=None, persist=True,
+ **kwargs):
+ id = id or uuid.uuid4().hex
+ timer = Timer.Timer(id, self, self._events, event_name, delay,
+ next_due, **kwargs)
+ if id:
+ timer.id = id
+ elif persist:
+ self.timer_setting(timer)
+ self.timers.append(timer)
+ def next_timer(self):
+ next = None
+ for timer in self.timers:
+ time_left = timer.time_left()
+ if next == None or time_left < next:
+ next = time_left
+
+ if next == None:
+ return None
+ if next < 0:
+ return 0
+ return next
+ def call_timers(self):
+ for timer in self.timers[:]:
+ if timer.due():
+ timer.call()
+ if timer.done():
+ self.timer_setting_remove(timer)
+ def next_send(self):
+ next = None
+ for server in self.servers.values():
+ timeout = server.send_throttle_timeout()
+ if server.waiting_send() and (next == None or timeout < next):
+ next = timeout
+ return next
+
+ def next_ping(self):
+ timeouts = []
+ for server in self.servers.values():
+ timeout = server.until_next_ping()
+ if not timeout == None:
+ timeouts.append(timeout)
+ if not timeouts:
+ return None
+ return min(timeouts)
+ def next_read_timeout(self):
+ timeouts = []
+ for server in self.servers.values():
+ timeouts.append(server.until_read_timeout())
+ if not timeouts:
+ return None
+ return min(timeouts)
+
+ def get_poll_timeout(self):
+ timeouts = []
+ timeouts.append(self.next_timer())
+ timeouts.append(self.next_send())
+ timeouts.append(self.next_ping())
+ timeouts.append(self.next_read_timeout())
+ return min([timeout for timeout in timeouts if not timeout == None])
+
+ def register_read(self, server):
+ self.poll.modify(server.fileno(), select.EPOLLIN)
+ def register_write(self, server):
+ self.poll.modify(server.fileno(), select.EPOLLOUT)
+ def register_both(self, server):
+ self.poll.modify(server.fileno(),
+ select.EPOLLIN|select.EPOLLOUT)
+
+ def disconnect(self, server):
+ try:
+ self.poll.unregister(server.fileno())
+ except FileNotFoundError:
+ pass
+ del self.servers[server.fileno()]
+
+ def reconnect(self, event):
+ server_details = self.database.servers.get(event["server_id"])
+ server = self.add_server(*(server_details + (False,)))
+ if self.connect(server):
+ self.servers[server.fileno()] = server
+ else:
+ event["timer"].redo()
+
+ def set_setting(self, setting, value):
+ self.database.bot_settings.set(setting, value)
+ def get_setting(self, setting, default=None):
+ return self.database.bot_settings.get(setting, default)
+ def find_settings(self, pattern, default=[]):
+ return self.database.bot_settings.find(pattern, default)
+ def find_settings_prefix(self, prefix, default=[]):
+ return self.database.bot_settings.find_prefix(
+ prefix, default)
+ def del_setting(self, setting):
+ self.database.bot_settings.delete(setting)
+
+ def run(self):
+ while self.running:
+ self.lock.acquire()
+ events = self.poll.poll(self.get_poll_timeout())
+ self.call_timers()
+ for fd, event in events:
+ if fd in self.servers:
+ server = self.servers[fd]
+ if event & select.EPOLLIN:
+ lines = server.read()
+ for line in lines:
+ if self.args.verbose:
+ self.log.info("<%s | %s", [str(server), line])
+ else:
+ self.log.debug("%s (raw) | %s", [str(server),
+ line])
+ server.parse_line(line)
+ elif event & select.EPOLLOUT:
+ server._send()
+ self.register_read(server)
+ elif event & select.EPULLHUP:
+ print("hangup")
+ server.disconnect()
+
+ for server in list(self.servers.values()):
+ if server.read_timed_out():
+ print("pingout from %s" % str(server))
+ server.disconnect()
+ elif server.ping_due() and not server.ping_sent:
+ server.send_ping()
+ server.ping_sent = True
+ if not server.connected:
+ self.disconnect(server)
+
+ reconnect_delay = self.config.get("reconnect-delay", 10)
+ self.add_timer("reconnect", reconnect_delay, None, None, False,
+ server_id=server.id)
+
+ print("disconnected from %s, reconnecting in %d seconds" % (
+ str(server), reconnect_delay))
+ elif server.waiting_send() and server.throttle_done():
+ self.register_both(server)
+ self.lock.release()