bot.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. from dataclasses import dataclass
  2. from typing import Any, Generic, TypeVar
  3. import asyncio
  4. import aiosqlite
  5. from .types import CommandConfiguration, Message, Response, Context
  6. # TODO logging
  7. RawMsg = TypeVar("RawMsg")
  8. @dataclass
  9. class Rollbot(Generic[RawMsg]):
  10. command_config: CommandConfiguration
  11. database_file: str
  12. def __post_init__(self):
  13. self.context = Context(
  14. config=self.read_config,
  15. respond=self.respond,
  16. database=lambda: aiosqlite.connect(self.database_file),
  17. )
  18. def read_config(self, key: str) -> Any:
  19. raise NotImplemented("Must be implemented by driver")
  20. def parse(self, incoming: RawMsg) -> Message:
  21. raise NotImplemented("Must be implemented by driver")
  22. async def respond(self, response: Response):
  23. raise NotImplemented("Must be implemented by driver")
  24. async def on_startup(self):
  25. await asyncio.gather(
  26. *[task(self.context) for task in self.command_config.startup]
  27. )
  28. async def on_shutdown(self):
  29. await asyncio.gather(
  30. *[task(self.context) for task in self.command_config.shutdown]
  31. )
  32. async def on_message(self, incoming: RawMsg):
  33. message = self.parse(incoming)
  34. if message.text is None:
  35. return
  36. cleaned = message.text.lstrip()
  37. if len(cleaned) == 0 or cleaned[0] not in self.command_config.bangs:
  38. return
  39. parts = cleaned[1:].lstrip().split(maxsplit=1)
  40. if len(parts) == 0:
  41. return
  42. command = self.command_config.aliases.get(parts[0], parts[0])
  43. res = self.command_config.call_and_response.get(command, None)
  44. if res is not None:
  45. await self.respond(Response.from_message(message, res))
  46. return
  47. command_call = self.command_config.commands.get(command, None)
  48. if command_call is None:
  49. await self.respond(
  50. Response.from_message(
  51. message, f"Sorry! I don't know the command {command}."
  52. )
  53. )
  54. return
  55. await command_call(message, self.context)