types.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. from __future__ import annotations
  2. from dataclasses import dataclass, field
  3. from datetime import datetime
  4. from collections.abc import Callable, Coroutine, Container
  5. from typing import Union, Any, Optional
  6. from aiosqlite.core import Connection
  7. @dataclass
  8. class Attachment:
  9. name: str
  10. body: Union[str, bytes]
  11. @dataclass
  12. class Message:
  13. origin_id: str
  14. channel_id: str
  15. sender_id: str
  16. timestamp: datetime
  17. origin_admin: bool
  18. channel_admin: bool
  19. text: Optional[str] = None
  20. attachments: list[Attachment] = field(default_factory=list)
  21. def __post_init__(self):
  22. self.command = None
  23. @dataclass
  24. class Command:
  25. bang: str
  26. name: str
  27. args: str
  28. @staticmethod
  29. def from_text(text: str) -> Optional[Command]:
  30. cleaned = text.lstrip()
  31. if len(cleaned) < 2:
  32. return None
  33. parts = cleaned[1:].lstrip().split(maxsplit=1)
  34. if len(parts) == 0:
  35. return None
  36. return Command(
  37. bang=cleaned[0],
  38. name=parts[0],
  39. args=parts[1] if len(parts) > 1 else "",
  40. )
  41. @dataclass
  42. class Response:
  43. origin_id: str
  44. channel_id: str
  45. text: Optional[str] = None
  46. attachments: Optional[list[Attachment]] = None
  47. @staticmethod
  48. def from_message(
  49. msg: Message, text: Optional[str] = None, attachments: list[Attachment] = None
  50. ) -> Response:
  51. return Response(
  52. origin_id=msg.origin_id,
  53. channel_id=msg.channel_id,
  54. text=text,
  55. attachments=attachments or [],
  56. )
  57. @dataclass
  58. class Context:
  59. config: Callable[[str], Any]
  60. respond: Callable[[], Coroutine[None, None, None]]
  61. database: Callable[[], Coroutine[None, None, Connection]]
  62. CommandType = Callable[[Message, Context], Coroutine[None, None, None]]
  63. StartupShutdownType = Callable[[Context], Coroutine[None, None, None]]
  64. @dataclass
  65. class CommandConfiguration:
  66. commands: dict[str, CommandType] = field(default_factory=dict)
  67. call_and_response: dict[str, str] = field(default_factory=dict)
  68. aliases: dict[str, str] = field(default_factory=dict)
  69. bangs: Container[str] = ("!",)
  70. startup: list[StartupShutdownType] = field(default_factory=list)
  71. shutdown: list[StartupShutdownType] = field(default_factory=list)
  72. def extend(self, other: CommandConfiguration) -> CommandConfiguration:
  73. return CommandConfiguration(
  74. commands={
  75. **self.commands,
  76. **other.commands,
  77. },
  78. call_and_response={
  79. **self.call_and_response,
  80. **other.call_and_response,
  81. },
  82. aliases={
  83. **self.aliases,
  84. **other.aliases,
  85. },
  86. bangs=(*self.bangs, *other.bangs),
  87. startup=[*self.startup, *other.startup],
  88. shutdown=[*self.shutdown, *other.shutdown],
  89. )