Release 0.9.0

This commit is contained in:
2025-11-21 07:26:02 +01:00
committed by ecv
commit 472f0812e7
240 changed files with 20033 additions and 0 deletions

1
bot/mixins/__init__.py Normal file
View File

@@ -0,0 +1 @@
__all__ = ["action", "template", "trigger", "widget"]

106
bot/mixins/action.py Normal file
View File

@@ -0,0 +1,106 @@
from os import path, listdir, pardir
from importlib import import_module
from threading import Thread
from bot import loaded_modules_dict
import string
import random
class Action(object):
available_actions_dict = dict
trigger_action_hook = object
def __init__(self):
self.available_actions_dict = {}
self.trigger_action_hook = self.trigger_action
def register_action(self, identifier, action_dict):
self.available_actions_dict[identifier] = action_dict
def enable_action(self, identifier):
self.available_actions_dict[identifier]["enabled"] = True
def disable_action(self, identifier):
self.available_actions_dict[identifier]["enabled"] = False
@staticmethod
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
@staticmethod
def get_all_available_actions_dict():
all_available_actions_dict = {}
for loaded_module_identifier, loaded_module in loaded_modules_dict.items():
if len(loaded_module.available_actions_dict) >= 1:
all_available_actions_dict[loaded_module_identifier] = loaded_module.available_actions_dict
return all_available_actions_dict
def import_actions(self):
modules_root_dir = path.join(path.dirname(path.abspath(__file__)), pardir, "modules")
module_actions_root_dir = path.join(modules_root_dir, self.options['module_name'], "actions")
try:
for module_action in listdir(module_actions_root_dir):
if module_action == 'common.py' or module_action == '__init__.py' or module_action[-3:] != '.py':
continue
import_module("bot.modules." + self.options['module_name'] + ".actions." + module_action[:-3])
except FileNotFoundError as error:
# module does not have actions
pass
@staticmethod
def trigger_action(target_module, event_data=None, dispatchers_steamid=None):
if event_data is None:
event_data = []
action_identifier = event_data[0]
if action_identifier in target_module.available_actions_dict:
server_is_online = target_module.dom.data.get("module_telnet", {}).get("server_is_online", False)
active_action = target_module.available_actions_dict[action_identifier]
action_requires_server_to_be_online = active_action.get(
"requires_telnet_connection", False
)
action_is_enabled = active_action.get("enabled", False)
user_has_permission = event_data[1].get("has_permission", None)
# permission is None = no status has been set, so it's allowed (default)
# permission is True = Permission has been set by some other process
# permission is False = permission has not been granted by any module
if dispatchers_steamid is not None:
# none would be a system-call
pass
if action_is_enabled:
event_data[1]["module"] = target_module.getName()
event_data[1]["uuid4"] = target_module.id_generator(22)
if server_is_online is True or action_requires_server_to_be_online is not True:
if any([
user_has_permission is None,
user_has_permission is True
]):
Thread(
target=active_action.get("main_function"),
args=(target_module, event_data, dispatchers_steamid)
).start()
else:
# in case we don't have permission, we call the fail callback. it then can determine what to do
# next
fail_callback = active_action.get("callback_fail")
Thread(
target=target_module.callback_fail(
fail_callback,
target_module,
event_data,
dispatchers_steamid
),
args=(target_module, event_data, dispatchers_steamid)
).start()
else:
try:
skip_it_callback = active_action.get("skip_it")
Thread(
target=skip_it_callback,
args=(target_module, event_data)
).start()
except KeyError:
pass

15
bot/mixins/template.py Normal file
View File

@@ -0,0 +1,15 @@
from os import path, pardir
import jinja2
class Template(object):
templates = object
def __init__(self):
pass
def import_templates(self):
modules_root_dir = path.join(path.dirname(path.abspath(__file__)), pardir, "modules")
modules_template_dir = path.join(modules_root_dir, self.options['module_name'], 'templates')
file_loader = jinja2.FileSystemLoader(modules_template_dir)
self.templates = jinja2.Environment(loader=file_loader)

62
bot/mixins/trigger.py Normal file
View File

@@ -0,0 +1,62 @@
from bot import loaded_modules_dict
from os import path, listdir, pardir
from importlib import import_module
import re
class Trigger(object):
available_triggers_dict = dict
def __init__(self):
self.available_triggers_dict = {}
def start(self):
try:
for name, triggers in self.available_triggers_dict.items():
try:
for trigger, handler in triggers["handlers"].items():
self.dom.data.register_callback(self, trigger, handler)
except KeyError:
pass
except KeyError as error:
pass
def register_trigger(self, identifier, trigger_dict):
self.available_triggers_dict[identifier] = trigger_dict
def import_triggers(self):
modules_root_dir = path.join(path.dirname(path.abspath(__file__)), pardir, "modules")
module_triggers_root_dir = path.join(modules_root_dir, self.options['module_name'])
try:
for module_trigger in listdir(path.join(module_triggers_root_dir, "triggers")):
if module_trigger == 'common.py' or module_trigger == '__init__.py' or module_trigger[-3:] != '.py':
continue
import_module("bot.modules." + self.options['module_name'] + ".triggers." + module_trigger[:-3])
for module_trigger in listdir(path.join(module_triggers_root_dir, "commands")):
if module_trigger == 'common.py' or module_trigger == '__init__.py' or module_trigger[-3:] != '.py':
continue
import_module("bot.modules." + self.options['module_name'] + ".commands." + module_trigger[:-3])
except FileNotFoundError:
pass
except ModuleNotFoundError:
pass
def execute_telnet_triggers(self):
telnet_lines_to_process = self.telnet.get_a_bunch_of_lines_from_queue(25)
for telnet_line in telnet_lines_to_process:
for loaded_module in loaded_modules_dict.values():
for trigger_name, trigger_group in loaded_module.available_triggers_dict.items():
try:
for trigger in trigger_group["triggers"]:
regex_results = re.search(trigger["regex"], telnet_line)
if regex_results:
trigger["callback"](loaded_module, self, regex_results)
# TODO: add method to append log, or create a new one
# TODO: this needs to weed out triggers being called too often
except KeyError:
pass

87
bot/mixins/widget.py Normal file
View File

@@ -0,0 +1,87 @@
from os import path, listdir, pardir
from importlib import import_module
from bot import loaded_modules_dict
class Widget(object):
available_widgets_dict = dict
template_render_hook = object
def __init__(self):
self.available_widgets_dict = {}
self.template_render_hook = self.template_render
def on_socket_connect(self, steamid):
if isinstance(self.available_widgets_dict, dict) and len(self.available_widgets_dict) >= 1:
for name, widget in self.available_widgets_dict.items():
if widget["main_widget"] is not None:
widget["main_widget"](self, dispatchers_steamid=steamid)
def on_socket_disconnect(self, steamid):
if isinstance(self.available_widgets_dict, dict) and len(self.available_widgets_dict) >= 1:
for name, widget in self.available_widgets_dict.items():
if widget["main_widget"] is not None:
widget["main_widget"](self, dispatchers_steamid=steamid)
def on_socket_event(self, event_data, dispatchers_steamid):
pass
@staticmethod
def template_render(*args, **kwargs):
try:
template = kwargs.get("template", None)
rendered_template = template.render(**kwargs)
except AttributeError as error:
rendered_template = ""
return rendered_template
@staticmethod
def get_all_available_widgets_dict():
all_available_widgets_dict = {}
for loaded_module_identifier, loaded_module in loaded_modules_dict.items():
if len(loaded_module.available_widgets_dict) >= 1:
all_available_widgets_dict[loaded_module_identifier] = loaded_module.available_widgets_dict
return all_available_widgets_dict
def start(self):
if isinstance(self.available_widgets_dict, dict) and len(self.available_widgets_dict) >= 1:
for name, widget in self.available_widgets_dict.items():
for trigger, handler in widget["handlers"].items():
self.dom.data.register_callback(self, trigger, handler)
def register_widget(self, identifier, widget_dict):
if widget_dict.get("enabled", True):
self.available_widgets_dict[identifier] = widget_dict
def import_widgets(self):
modules_root_dir = path.join(path.dirname(path.abspath(__file__)), pardir, "modules")
module_widgets_root_dir = path.join(modules_root_dir, self.options['module_name'], "widgets")
try:
for module_widget in listdir(module_widgets_root_dir):
if module_widget == 'common.py' or module_widget == '__init__.py' or module_widget[-3:] != '.py':
continue
import_module("bot.modules." + self.options['module_name'] + ".widgets." + module_widget[:-3])
except FileNotFoundError as error:
# module does not have widgets
pass
def get_current_view(self, dispatchers_steamid):
return (
self.dom.data
.get(self.get_module_identifier(), {})
.get("visibility", {})
.get(dispatchers_steamid, {})
.get("current_view", "frontend")
)
def set_current_view(self, dispatchers_steamid, options):
self.dom.data.upsert({
self.get_module_identifier(): {
"visibility": {
dispatchers_steamid: options
}
}
}, dispatchers_steamid=dispatchers_steamid)