discord_driver.py 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. from __future__ import annotations
  2. import io
  3. import asyncio
  4. import logging.config
  5. import os
  6. import toml
  7. import discord
  8. import rollbot
  9. from commands import config
  10. logging.config.fileConfig("logging.conf", disable_existing_loggers=False)
  11. with open(os.environ.get("SECRET_FILE", "secrets.toml"), "r") as sfile:
  12. secrets = toml.load(sfile)
  13. database_file = os.environ.get("DATABASE_FILE", secrets["database_file"])
  14. class DiscordBot(rollbot.Rollbot[discord.Message]):
  15. def __init__(self, client):
  16. super().__init__(config.extend(rollbot.CommandConfiguration(bangs=("!",))), database_file)
  17. self.discord_client = client
  18. client.event(self.on_message)
  19. def read_config(self, key: str) -> Any:
  20. cfg = secrets
  21. for part in key.split("."):
  22. cfg = cfg.get(part, None)
  23. if cfg is None:
  24. return None
  25. return cfg
  26. async def parse(self, msg: discord.Message) -> rollbot.Message:
  27. return rollbot.Message(
  28. origin_id="DISCORD",
  29. channel_id=str(msg.channel.id),
  30. sender_id=str(msg.author.id),
  31. timestamp=msg.created_at,
  32. origin_admin="RollbotAdmin" in [r.name for r in msg.author.roles],
  33. channel_admin=False, # TODO - implement this if discord allows it
  34. sender_name=msg.author.name,
  35. text=msg.content,
  36. # TODO might be nice to only read attachments once we're sure we need to
  37. attachments=[rollbot.Attachment(
  38. name=att.filename,
  39. body=await att.read(),
  40. ) for att in msg.attachments],
  41. )
  42. async def respond(self, response: rollbot.Response):
  43. if response.origin_id != "DISCORD":
  44. self.context.logger.error(f"Unable to respond to {response.origin_id}")
  45. return
  46. channel = await self.discord_client.fetch_channel(response.channel_id)
  47. content=response.text or ""
  48. # TODO this implementation is wrong - attaches files instead of embedding an image
  49. # I *think* the right call is to send multiple messages since there's only one embed each
  50. # but I want to do a little more research before implementing that
  51. files = []
  52. if response.attachments is not None:
  53. for att in response.attachments:
  54. if att.name == "image":
  55. if isinstance(att.body, bytes):
  56. files.append(discord.File(io.BytesIO(att.body)))
  57. else:
  58. content += "\n" + att.body
  59. await channel.send(content=content, files=files)
  60. if __name__ == "__main__":
  61. loop = asyncio.get_event_loop()
  62. client = discord.Client(loop=loop)
  63. bot = DiscordBot(client)
  64. try:
  65. loop.run_until_complete(bot.on_startup())
  66. loop.run_until_complete(client.start(secrets["discord"]["token"]))
  67. except KeyboardInterrupt:
  68. loop.run_until_complete(client.close())
  69. finally:
  70. loop.run_until_complete(bot.on_shutdown())