plugins.py 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import logging
  2. import inspect
  3. from functools import reduce
  4. from .messaging import RollbotResponse, RollbotMessage, RollbotFailure
  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 get_converters(parameters, annotations):
  24. converters = []
  25. for p in parameters:
  26. if p in ("msg", "message", "_msg"):
  27. converters.append(lambda cmd, db, msg: msg)
  28. elif p in ("db", "database"):
  29. converters.append(lambda cmd, db, msg: db)
  30. elif p in ("log", "logger"):
  31. converters.append(lambda cmd, db, msg: cmd.logger)
  32. elif p in ("bot", "rollbot"):
  33. converters.append(lambda cmd, db, msg: cmd.bot)
  34. elif p in ("subc", "subcommand"):
  35. converters.append(lambda cmd, db, msg: RollbotMessage.from_subcommand(msg))
  36. elif p.startswith("data") or p.endswith("data"):
  37. annot = annotations.get(p, p)
  38. converters.append(lambda cmd, db, msg, sing_cls=annot: sing_cls.get_or_create(db, msg))
  39. else:
  40. raise ValueError(p)
  41. return converters
  42. def as_plugin(command):
  43. if isinstance(command, str):
  44. command_name = command
  45. else:
  46. command_name = command.__name__
  47. def init_standin(self, bot, logger=logging.getLogger(__name__)):
  48. RollbotPlugin.__init__(self, command_name, bot, logger=logger)
  49. def decorator(fn):
  50. sig = inspect.signature(fn)
  51. try:
  52. converters = get_converters(sig.parameters, fn.__annotations__)
  53. except ValueError as ve:
  54. raise ValueError(f"Illegal argument name {str(ve)} in decorated plugin {command_name}")
  55. def on_command_standin(self, db, msg):
  56. res = fn(*[c(self, db, msg) for c in converters])
  57. if res is None:
  58. return RollbotResponse(msg, respond=False)
  59. elif isinstance(res, RollbotResponse):
  60. return res
  61. elif isinstance(res, RollbotFailure):
  62. return RollbotResponse(msg, failure=res, debugging=res.get_debugging())
  63. else:
  64. return RollbotResponse(msg, txt=str(res))
  65. return type(
  66. f"AutoGenerated`{command_name}`Command",
  67. (RollbotPlugin,),
  68. dict(
  69. __init__=init_standin,
  70. on_command=on_command_standin,
  71. )
  72. )
  73. if isinstance(command, str):
  74. return decorator
  75. else:
  76. return decorator(command)