plugins.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import logging
  2. import inspect
  3. from functools import reduce
  4. from .messaging import RollbotResponse, RollbotMessage
  5. class RollbotPlugin:
  6. def __init__(self, command, bot, logger=logging.getLogger(__name__)):
  7. self.command = command
  8. self.bot = bot
  9. self.logger = logger
  10. self.logger.info(f"Intializing {type(self).__name__} matching {command}")
  11. def on_start(self, db):
  12. self.logger.info(f"No on_start initialization of {type(self).__name__}")
  13. def on_shutdown(self, db):
  14. self.logger.info(f"No on_shutdown de-initialization of {type(self).__name__}")
  15. def on_command(self, db, message):
  16. raise NotImplementedError
  17. @staticmethod
  18. def find_all_plugins():
  19. def get_subclasses(clazz):
  20. s = set(clazz.__subclasses__())
  21. return s | reduce(set.union, (get_subclasses(sc) for sc in s), set())
  22. return get_subclasses(RollbotPlugin)
  23. def as_plugin(command):
  24. if isinstance(command, str):
  25. command_name = command
  26. else:
  27. command_name = command.__name__
  28. def init_standin(self, bot, logger=logging.getLogger(__name__)):
  29. RollbotPlugin.__init__(self, command_name, bot, logger=logger)
  30. def decorator(fn):
  31. sig = inspect.signature(fn)
  32. converters = []
  33. for p in sig.parameters:
  34. if p in ("msg", "message", "_msg"):
  35. converters.append(lambda cmd, db, msg: msg)
  36. elif p in ("db", "database"):
  37. converters.append(lambda cmd, db, msg: db)
  38. elif p in ("log", "logger"):
  39. converters.append(lambda cmd, db, msg: cmd.logger)
  40. elif p in ("bot", "rollbot"):
  41. converters.append(lambda cmd, db, msg: cmd.bot)
  42. elif p in ("subc", "subcommand"):
  43. converters.append(lambda cmd, db, msg: RollbotMessage.from_subcommand(msg))
  44. elif p.startswith("data") or p.endswith("data"):
  45. annot = fn.__annotations__.get(p, p)
  46. converters.append(lambda cmd, db, msg, sing_cls=annot: sing_cls.get_or_create(db, msg))
  47. else:
  48. raise ValueError(f"Illegal argument name {p} in decorated plugin {command_name}")
  49. def on_command_standin(self, db, msg):
  50. res = fn(*[c(self, db, msg) for c in converters])
  51. if isinstance(res, RollbotResponse):
  52. return res
  53. else:
  54. return RollbotResponse(msg, txt=str(res))
  55. return type(
  56. f"AutoGenerated`{command_name}`Command",
  57. (RollbotPlugin,),
  58. dict(
  59. __init__=init_standin,
  60. on_command=on_command_standin,
  61. )
  62. )
  63. if isinstance(command, str):
  64. return decorator
  65. else:
  66. return decorator(command)