types.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. from dataclasses import dataclass, field
  2. from datetime import datetime
  3. from collections.abc import Callable, Coroutine, Container
  4. from typing import Union, Any, Optional
  5. from __future__ import annotations
  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. )