Initial Release of IGNCore version 2.5
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
import inspect
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
from core.decorators import instance
|
||||
from core.functions import get_attrs
|
||||
from core.logger import Logger
|
||||
from core.registry import Registry
|
||||
|
||||
|
||||
@instance()
|
||||
class EventService:
|
||||
def __init__(self):
|
||||
self.handlers = {}
|
||||
self.logger = Logger(__name__)
|
||||
self.event_types = []
|
||||
self.db_cache = {}
|
||||
|
||||
def inject(self, registry):
|
||||
self.db = registry.get_instance("db")
|
||||
self.bot = registry.get_instance("bot")
|
||||
self.util = registry.get_instance("util")
|
||||
|
||||
def pre_start(self):
|
||||
self.register_event_type("timer")
|
||||
|
||||
def start(self):
|
||||
# process decorators
|
||||
for _, inst in Registry.get_all_instances().items():
|
||||
for name, method in get_attrs(inst).items():
|
||||
if hasattr(method, "event"):
|
||||
key = Registry.get_module_name(inst).split(".")
|
||||
# We dont want to load events, if their modules not enabled in our config...
|
||||
if key[0] not in self.bot.modules:
|
||||
continue
|
||||
attrs = getattr(method, "event")
|
||||
handler = getattr(inst, name)
|
||||
self.register(handler, attrs.event_type, attrs.description, inst.module_name, attrs.is_hidden,
|
||||
attrs.is_enabled)
|
||||
|
||||
def register_event_type(self, event_type):
|
||||
"""
|
||||
Call during pre_start
|
||||
|
||||
Args:
|
||||
event_type (str)
|
||||
"""
|
||||
|
||||
event_type = event_type.lower()
|
||||
|
||||
if event_type in self.event_types:
|
||||
self.logger.error("Could not register event type '%s': event type already registered" % event_type)
|
||||
return
|
||||
|
||||
self.logger.debug("Registering event type '%s'" % event_type)
|
||||
self.event_types.append(event_type)
|
||||
|
||||
def is_event_type(self, event_base_type):
|
||||
return event_base_type in self.event_types
|
||||
|
||||
def register(self, handler, event_type, description, module, is_hidden, is_enabled):
|
||||
"""
|
||||
Call during pre_start
|
||||
|
||||
Args:
|
||||
handler: (event_type, event_data) -> void
|
||||
event_type: str
|
||||
description: str
|
||||
module: str
|
||||
is_hidden: bool
|
||||
is_enabled: bool
|
||||
"""
|
||||
if len(inspect.signature(handler).parameters) != 2:
|
||||
raise Exception(
|
||||
"Incorrect number of arguments for handler '%s.%s()'" % (handler.__module__, handler.__name__))
|
||||
|
||||
event_base_type, event_sub_type = self.get_event_type_parts(event_type)
|
||||
module = module.lower()
|
||||
handler_name = self.util.get_handler_name(handler)
|
||||
is_hidden = 1 if is_hidden else 0
|
||||
is_enabled = 1 if is_enabled else 0
|
||||
if event_base_type not in self.event_types:
|
||||
self.logger.error("Could not register handler '%s' for event type '%s': event type does not exist" % (
|
||||
handler_name, event_type))
|
||||
return
|
||||
|
||||
if not description:
|
||||
self.logger.warning("No description for event_type '%s' and handler '%s'" % (event_type, handler_name))
|
||||
|
||||
row = self.db.query_single("SELECT 1 FROM event_config WHERE event_type = ? AND handler = ?",
|
||||
[event_base_type, handler_name])
|
||||
if row is None:
|
||||
# add new event commands
|
||||
self.db.exec(
|
||||
"INSERT INTO event_config (event_type, event_sub_type, handler, "
|
||||
"description, module, enabled, verified, is_hidden) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
[event_base_type, event_sub_type, handler_name, description, module, is_enabled, 1, is_hidden])
|
||||
if event_base_type == "timer":
|
||||
self.db.exec(
|
||||
"INSERT INTO timer_event (event_type, event_sub_type, handler, next_run) VALUES (?, ?, ?, ?)",
|
||||
[event_base_type, event_sub_type, handler_name, int(time.time())])
|
||||
else:
|
||||
# mark command as verified
|
||||
self.db.exec(
|
||||
"UPDATE event_config SET verified = ?, module = ?, description = ?, event_sub_type = ?, is_hidden = ? "
|
||||
"WHERE event_type = ? AND handler = ?",
|
||||
[1, module, description, event_sub_type, is_hidden, event_base_type, handler_name])
|
||||
|
||||
if event_base_type == "timer":
|
||||
self.db.exec("UPDATE timer_event SET event_sub_type = ? WHERE event_type = ? AND handler = ?",
|
||||
[event_sub_type, event_base_type, handler_name])
|
||||
|
||||
# load command handler
|
||||
self.handlers[handler_name] = handler
|
||||
|
||||
def fire_event(self, event_type, event_data=None):
|
||||
event_base_type, event_sub_type = self.get_event_type_parts(event_type)
|
||||
|
||||
if event_base_type not in self.event_types:
|
||||
self.logger.error("Could not fire event type '%s': event type does not exist" % event_type)
|
||||
return
|
||||
|
||||
data = self.get_handlers(event_base_type, event_sub_type)
|
||||
for row in data:
|
||||
if event_type != "connect":
|
||||
t = Thread(target=self.call_handler, args=(row.handler, event_type, event_data), daemon=True)
|
||||
t.run()
|
||||
else:
|
||||
self.call_handler(row.handler, event_type, event_data)
|
||||
|
||||
def call_handler(self, handler_method, event_type, event_data):
|
||||
handler = self.handlers.get(handler_method, None)
|
||||
if not handler:
|
||||
self.logger.error(
|
||||
"Could not find handler callback for event type '%s' and handler '%s'" % (event_type, handler_method))
|
||||
return
|
||||
|
||||
try:
|
||||
handler(event_type, event_data)
|
||||
except Exception as e:
|
||||
self.logger.error("error processing event '%s'" % event_type, e)
|
||||
|
||||
def get_event_type_parts(self, event_type):
|
||||
parts = event_type.lower().split(":", 1)
|
||||
if len(parts) == 2:
|
||||
return parts[0], parts[1]
|
||||
else:
|
||||
return parts[0], ""
|
||||
|
||||
def get_event_type_key(self, event_base_type, event_sub_type):
|
||||
return event_base_type + ":" + event_sub_type
|
||||
|
||||
def check_for_timer_events(self, current_timestamp):
|
||||
data = self.db.query("SELECT e.event_type, e.event_sub_type, e.handler, t.next_run FROM timer_event t "
|
||||
"JOIN event_config e ON t.event_type = e.event_type AND t.handler = e.handler "
|
||||
"WHERE t.next_run <= ? AND e.enabled = 1", [current_timestamp])
|
||||
for row in data:
|
||||
self.execute_timed_event(row, current_timestamp)
|
||||
|
||||
def execute_timed_event(self, row, current_timestamp):
|
||||
event_type_key = self.get_event_type_key(row.event_type, row.event_sub_type)
|
||||
|
||||
# timer event run times should be consistent, so we base the next run time off the last run time,
|
||||
# instead of the current timestamp
|
||||
next_run = row.next_run + int(row.event_sub_type)
|
||||
|
||||
# prevents timer events from getting too far behind, or having a large "catch-up" after
|
||||
# the bot has been offline for a time
|
||||
if next_run < current_timestamp:
|
||||
next_run = current_timestamp + int(row.event_sub_type)
|
||||
|
||||
self.db.exec("UPDATE timer_event SET next_run = ? WHERE event_type = ? AND handler = ?",
|
||||
[next_run, row.event_type, row.handler])
|
||||
|
||||
self.call_handler(row.handler, event_type_key, None)
|
||||
|
||||
def update_event_status(self, event_base_type, event_sub_type, event_handler, enabled_status):
|
||||
# clear cache
|
||||
self.db_cache[event_base_type + ":" + event_sub_type] = None
|
||||
|
||||
return self.db.exec(
|
||||
"UPDATE event_config SET enabled = ? WHERE event_type = ? AND event_sub_type = ? AND handler LIKE ?",
|
||||
[enabled_status, event_base_type, event_sub_type, event_handler])
|
||||
|
||||
def get_event_types(self):
|
||||
return self.event_types
|
||||
|
||||
def get_handlers(self, event_base_type, event_sub_type):
|
||||
# check first in cache
|
||||
result = self.db_cache.get(event_base_type + ":" + event_sub_type, None)
|
||||
if result is not None:
|
||||
return result
|
||||
else:
|
||||
result = self.db.query(
|
||||
"SELECT handler FROM event_config WHERE event_type = ? AND event_sub_type = ? AND enabled = 1",
|
||||
[event_base_type, event_sub_type])
|
||||
|
||||
# store result in cache
|
||||
self.db_cache[event_base_type + ":" + event_sub_type] = result
|
||||
|
||||
return result
|
||||
|
||||
def run_timer_events_at_startup(self):
|
||||
t = int(time.time())
|
||||
data = self.db.query("SELECT e.event_type, e.event_sub_type, e.handler, t.next_run FROM timer_event t "
|
||||
"JOIN event_config e ON t.event_type = e.event_type AND t.handler = e.handler "
|
||||
"WHERE e.event_type = ? AND e.enabled = 1", ["timer"])
|
||||
for row in data:
|
||||
handler = self.handlers[row.handler]
|
||||
attrs = getattr(handler, "event")
|
||||
if attrs.get("run_at_startup", False):
|
||||
self.execute_timed_event(row, t)
|
||||
Reference in New Issue
Block a user