浏览代码

Dynamically loading plugins, no configuration needed

Kirk Trombley 6 年之前
父节点
当前提交
719722d17a
共有 6 个文件被更改,包括 37 次插入37 次删除
  1. 0 11
      config/config.toml
  2. 7 4
      src/app.py
  3. 1 1
      src/command_system.py
  4. 9 0
      src/plugins/__init__.py
  5. 11 21
      src/rollbot.py
  6. 9 0
      src/util.py

+ 0 - 11
config/config.toml

@@ -1,17 +1,6 @@
 database = "/tmp/rollbot.sqlite"
 sleep_time = 1.5
 
-[plugins]
-simple = [ "isadmin", "debug", "echo", "greet", "console" ]
-teamspeak = [ "teamspeak", "speamteek", "teamscream", ]
-session = [ "session" ]
-roll = [ "roll" ]
-querying = [ "inspire", "shield", "scp", "riddle" ]
-seychelles = [ "seychelles" ]
-curse = [ "curse", "bless" ]
-the_guy = [ "the_guy" ]
-the_house = [ "the_house", "shut_up" ]
-
 [aliases]
 speemteek = "speamteek"
 speemteak = "speamteek"

+ 7 - 4
src/app.py

@@ -1,13 +1,15 @@
 import atexit
 from logging.config import dictConfig
+from threading import Thread
 
 from flask import Flask, request, render_template
 
 import db
 from config import BOTS_LOOKUP, get_config, get_secret
-from command_system import RollbotMessage
+from command_system import RollbotMessage, RollbotPlugin
 from rollbot import Rollbot
-from util import post_message
+from util import post_message, find_all_subclasses
+import plugins
 
 GLOBAL_ADMINS = get_secret("auths.global")
 GROUP_ADMINS = get_secret("auths.group")
@@ -32,7 +34,7 @@ app = Flask(__name__)
 app.config["PROPAGATE_EXCEPTIONS"] = True
 rollbot = Rollbot(
     logger=app.logger,
-    plugins=get_config("plugins"),
+    plugin_classes=find_all_subclasses(RollbotPlugin),
     aliases=get_config("aliases"),
     responses=get_config("responses"),
     lookup=BOTS_LOOKUP,
@@ -61,7 +63,8 @@ def teamspeak():
 def execute():
     json = request.get_json()
     msg = RollbotMessage.from_groupme(json, global_admins=GLOBAL_ADMINS, group_admins=GROUP_ADMINS)
-    rollbot.handle_command_threaded(msg)
+    t = Thread(target=lambda: rollbot.handle_command(msg))
+    t.start()
     return "", 204
 
 

+ 1 - 1
src/command_system.py

@@ -143,7 +143,7 @@ def as_plugin(command):
 
     def decorator(fn):
         return type(
-            f"AutoGenerated`{fn.__name__}`Command",
+            f"AutoGenerated`{command_name}`Command",
             (RollbotPlugin,),
             dict(
                 __init__=init_standin,

+ 9 - 0
src/plugins/__init__.py

@@ -0,0 +1,9 @@
+import plugins.roll
+import plugins.querying
+import plugins.seychelles
+import plugins.the_guy
+import plugins.session
+import plugins.simple
+import plugins.curse
+import plugins.teamspeak
+import plugins.the_house

+ 11 - 21
src/rollbot.py

@@ -1,7 +1,5 @@
-import importlib
 import logging
 import time
-from threading import Thread
 
 from command_system import RollbotResponse, RollbotFailure, as_plugin
 
@@ -14,7 +12,7 @@ def lift_response(call, response):
 
 
 class Rollbot:
-    def __init__(self, logger=logging.getLogger(__name__), plugins={}, aliases={}, responses={}, lookup={}, sleep_time=0.0, session_factory=None, callback=None):
+    def __init__(self, logger=logging.getLogger(__name__), plugin_classes={}, aliases={}, responses={}, lookup={}, sleep_time=0.0, session_factory=None, callback=None):
         self.logger = logger
         self.session_factory = session_factory or (lambda: None)
         self.callback = callback or (lambda txt, gid: self.logger.info(f"Responding to {gid} with {txt}"))
@@ -23,20 +21,16 @@ class Rollbot:
         self.to_stop = set()
 
         self.logger.info("Loading command plugins")
-        for module, classes in plugins.items():
-            plugin_module = importlib.import_module("plugins." + module)
-            for class_name in classes:
-                self.logger.info(class_name)
-                plugin_class = getattr(plugin_module, class_name)
-                plugin_instance = plugin_class(logger=logger)
-                if plugin_instance.command in self.commands:
-                    self.logger.error(f"Duplicate command word '{plugin_instance.command}'")
-                    raise ValueError(f"Duplicate command word '{plugin_instance.command}'")
-                self.commands[plugin_instance.command] = plugin_instance
-                if "on_start" in plugin_class.__dict__:
-                    self.to_start.add(plugin_instance)
-                if "on_shutdown" in plugin_class.__dict__:
-                    self.to_stop.add(plugin_instance)
+        for plugin_class in plugin_classes:
+            plugin_instance = plugin_class(logger=logger)
+            if plugin_instance.command in self.commands:
+                self.logger.error(f"Duplicate command word '{plugin_instance.command}'")
+                raise ValueError(f"Duplicate command word '{plugin_instance.command}'")
+            self.commands[plugin_instance.command] = plugin_instance
+            if "on_start" in plugin_class.__dict__:
+                self.to_start.add(plugin_instance)
+            if "on_shutdown" in plugin_class.__dict__:
+                self.to_stop.add(plugin_instance)
         self.logger.info(f"Finished loading plugins, {len(self.commands)} commands found")
 
         self.logger.info("Loading simple responses")
@@ -135,7 +129,3 @@ class Rollbot:
 
         t = time.time() - t
         self.logger.info(f"Exiting command thread for {message.message_id} after {t:.3f}s")
-
-    def handle_command_threaded(self, message):
-        t = Thread(target=lambda: self.handle_command(message))
-        t.start()

+ 9 - 0
src/util.py

@@ -1,3 +1,5 @@
+from functools import reduce
+
 import requests
 from requests.exceptions import ConnectionError
 
@@ -33,3 +35,10 @@ def upload_image(key, content):
             "explain": "Could not parse response from GroupMe image service.",
             "exception": e
         }
+
+
+def find_all_subclasses(baseClass):
+    def get_subclasses(clazz):
+        s = set(clazz.__subclasses__())
+        return s | reduce(set.union, (get_subclasses(sc) for sc in s), set())
+    return get_subclasses(baseClass)