letschatty 0.1.44__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. letschatty-0.1.44/LICENSE +18 -0
  2. letschatty-0.1.44/PKG-INFO +37 -0
  3. letschatty-0.1.44/README.md +20 -0
  4. letschatty-0.1.44/pyproject.toml +19 -0
  5. letschatty-0.1.44/src/letschatty/__init__.py +2 -0
  6. letschatty-0.1.44/src/letschatty/models/__init__.py +4 -0
  7. letschatty-0.1.44/src/letschatty/models/company/empresa.py +11 -0
  8. letschatty-0.1.44/src/letschatty/models/events/__init__.py +6 -0
  9. letschatty-0.1.44/src/letschatty/models/events/__pycache__/__init__.cpython-311.pyc +0 -0
  10. letschatty-0.1.44/src/letschatty/models/events/base.py +45 -0
  11. letschatty-0.1.44/src/letschatty/models/events/contact_point.py +18 -0
  12. letschatty-0.1.44/src/letschatty/models/events/event_types.py +11 -0
  13. letschatty-0.1.44/src/letschatty/models/events/new_chat.py +11 -0
  14. letschatty-0.1.44/src/letschatty/models/events/quality_scoring.py +16 -0
  15. letschatty-0.1.44/src/letschatty/models/events/sale.py +16 -0
  16. letschatty-0.1.44/src/letschatty/models/events/template.py +19 -0
  17. letschatty-0.1.44/src/letschatty/models/messages/__init__.py +2 -0
  18. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__init__.py +17 -0
  19. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/__init__.cpython-311.pyc +0 -0
  20. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/audio.cpython-311.pyc +0 -0
  21. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/button.cpython-311.pyc +0 -0
  22. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/central_notification.cpython-311.pyc +0 -0
  23. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/contact.cpython-311.pyc +0 -0
  24. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/document.cpython-311.pyc +0 -0
  25. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/image.cpython-311.pyc +0 -0
  26. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/location.cpython-311.pyc +0 -0
  27. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/reaction.cpython-311.pyc +0 -0
  28. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/sticker.cpython-311.pyc +0 -0
  29. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/sticket.cpython-311.pyc +0 -0
  30. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/text.cpython-311.pyc +0 -0
  31. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/unsupported.cpython-311.pyc +0 -0
  32. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/__pycache__/video.cpython-311.pyc +0 -0
  33. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/audio.py +7 -0
  34. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/base/__init__.py +1 -0
  35. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/base/__pycache__/__init__.cpython-311.pyc +0 -0
  36. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/base/__pycache__/message_base.cpython-311.pyc +0 -0
  37. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/base/chatty_message_json.py +22 -0
  38. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/base/message_base.py +117 -0
  39. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/button.py +7 -0
  40. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/central_notification.py +8 -0
  41. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/contact.py +7 -0
  42. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/document.py +7 -0
  43. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/image.py +7 -0
  44. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/interactive.py +6 -0
  45. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/location.py +7 -0
  46. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/reaction.py +7 -0
  47. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__init__.py +18 -0
  48. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/__init__.cpython-311.pyc +0 -0
  49. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_audio.cpython-311.pyc +0 -0
  50. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_central.cpython-311.pyc +0 -0
  51. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_contacts.cpython-311.pyc +0 -0
  52. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_document.cpython-311.pyc +0 -0
  53. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_image.cpython-311.pyc +0 -0
  54. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_location.cpython-311.pyc +0 -0
  55. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_reaction.cpython-311.pyc +0 -0
  56. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_sticker.cpython-311.pyc +0 -0
  57. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_text.cpython-311.pyc +0 -0
  58. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/__pycache__/content_video.cpython-311.pyc +0 -0
  59. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_audio.py +6 -0
  60. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_central.py +5 -0
  61. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_contacts.py +21 -0
  62. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_document.py +8 -0
  63. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_image.py +4 -0
  64. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_location.py +5 -0
  65. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_media.py +8 -0
  66. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_reaction.py +4 -0
  67. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_sticker.py +6 -0
  68. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_text.py +5 -0
  69. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_content/content_video.py +5 -0
  70. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_context/__init__.py +0 -0
  71. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_context/__pycache__/chatty_context.cpython-311.pyc +0 -0
  72. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_context/chatty_context.py +22 -0
  73. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_referal/__init__.py +0 -0
  74. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/schema/chatty_referal/chatty_referal.py +37 -0
  75. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/sticker.py +7 -0
  76. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/text.py +7 -0
  77. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/unknown.py +6 -0
  78. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/unsupported.py +5 -0
  79. letschatty-0.1.44/src/letschatty/models/messages/chatty_messages/video.py +7 -0
  80. letschatty-0.1.44/src/letschatty/models/messages/messages_request/__init__.py +1 -0
  81. letschatty-0.1.44/src/letschatty/models/messages/messages_request/__pycache__/message_request.cpython-311.pyc +0 -0
  82. letschatty-0.1.44/src/letschatty/models/messages/messages_request/message_request.py +22 -0
  83. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/__pycache__/meta_base_notification_json.cpython-311.pyc +0 -0
  84. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/__pycache__/meta_error_json.cpython-311.pyc +0 -0
  85. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/__pycache__/meta_inexistent_json.cpython-311.pyc +0 -0
  86. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/__pycache__/meta_message_json.cpython-311.pyc +0 -0
  87. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/__pycache__/meta_message_types.cpython-311.pyc +0 -0
  88. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/__pycache__/meta_status_json.cpython-311.pyc +0 -0
  89. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/__pycache__/meta_unknown_json.cpython-311.pyc +0 -0
  90. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/meta_base_notification_json.py +64 -0
  91. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/meta_error_json.py +10 -0
  92. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/meta_inexistent_json.py +5 -0
  93. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/meta_message_json.py +142 -0
  94. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/meta_message_types.py +126 -0
  95. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/meta_status_json.py +85 -0
  96. letschatty-0.1.44/src/letschatty/models/messages/meta_message_model/meta_unknown_json.py +5 -0
  97. letschatty-0.1.44/src/letschatty/models/metrics/__init_.py +5 -0
  98. letschatty-0.1.44/src/letschatty/models/metrics/daily_contact_points.py +21 -0
  99. letschatty-0.1.44/src/letschatty/models/metrics/daily_new_chats.py +12 -0
  100. letschatty-0.1.44/src/letschatty/models/metrics/daily_sent_templates_grouped.py +21 -0
  101. letschatty-0.1.44/src/letschatty/models/metrics/sales_roadmap.py +15 -0
  102. letschatty-0.1.44/src/letschatty/models/metrics/sent_templates.py +19 -0
  103. letschatty-0.1.44/src/letschatty/models/utils/__init__.py +1 -0
  104. letschatty-0.1.44/src/letschatty/models/utils/__pycache__/__init__.cpython-311.pyc +0 -0
  105. letschatty-0.1.44/src/letschatty/models/utils/types/__init__.py +8 -0
  106. letschatty-0.1.44/src/letschatty/models/utils/types/__pycache__/__init__.cpython-311.pyc +0 -0
  107. letschatty-0.1.44/src/letschatty/models/utils/types/__pycache__/country.cpython-311.pyc +0 -0
  108. letschatty-0.1.44/src/letschatty/models/utils/types/__pycache__/identifier.cpython-311.pyc +0 -0
  109. letschatty-0.1.44/src/letschatty/models/utils/types/__pycache__/message_statuses.cpython-311.pyc +0 -0
  110. letschatty-0.1.44/src/letschatty/models/utils/types/__pycache__/phone_number.cpython-311.pyc +0 -0
  111. letschatty-0.1.44/src/letschatty/models/utils/types/__pycache__/source_types.cpython-311.pyc +0 -0
  112. letschatty-0.1.44/src/letschatty/models/utils/types/__pycache__/typealiases.cpython-311.pyc +0 -0
  113. letschatty-0.1.44/src/letschatty/models/utils/types/channel_types.py +5 -0
  114. letschatty-0.1.44/src/letschatty/models/utils/types/country.py +20 -0
  115. letschatty-0.1.44/src/letschatty/models/utils/types/identifier.py +15 -0
  116. letschatty-0.1.44/src/letschatty/models/utils/types/message_status_types.py +8 -0
  117. letschatty-0.1.44/src/letschatty/models/utils/types/message_types.py +20 -0
  118. letschatty-0.1.44/src/letschatty/models/utils/types/phone_number.py +14 -0
  119. letschatty-0.1.44/src/letschatty/models/utils/types/source_types.py +9 -0
  120. letschatty-0.1.44/src/letschatty/models/utils/types/typealiases.py +1 -0
  121. letschatty-0.1.44/src/letschatty/services/factories/__init__.py +1 -0
  122. letschatty-0.1.44/src/letschatty/services/factories/child_chatty_message_factory.py +1 -0
  123. letschatty-0.1.44/src/letschatty/services/factories/child_db_message_factory.py +108 -0
  124. letschatty-0.1.44/src/letschatty/services/factories/child_request_message.py +97 -0
  125. letschatty-0.1.44/src/letschatty/services/factories/parent_message_factory.py +18 -0
@@ -0,0 +1,18 @@
1
+ Proprietary and Confidential
2
+
3
+ Copyright (c) 2024 Axel Gualda. All Rights Reserved.
4
+
5
+ This software, known as "Chatty Analytics", and its documentation are the proprietary and confidential
6
+ property of Axel Gualda. The software is provided solely for the use of authorized individuals in
7
+ accordance with their duties and obligations related to Chatty Analytics.
8
+
9
+ Unauthorized copying, distribution, modification, public display, or public performance of this software,
10
+ its documentation, or any portion thereof is strictly prohibited. Proprietary and confidential information
11
+ contained within the software may not be copied, distributed, modified, or disclosed to any third party
12
+ without the prior written consent of Axel Gualda.
13
+
14
+ This software is provided "AS IS", without warranty of any kind, express or implied, including but not
15
+ limited to the warranties of merchantability, fitness for a particular purpose and non-infringement.
16
+ In no event shall the author or copyright holder be liable for any claim, damages or other liability,
17
+ whether in an action of contract, tort or otherwise, arising from, out of or in connection with the
18
+ software or the use or other dealings in the software.
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.1
2
+ Name: letschatty
3
+ Version: 0.1.44
4
+ Summary: Models and custom classes to work across the Chattyverse
5
+ License: MIT
6
+ Author: Axel
7
+ Author-email: axel@letschatty.com
8
+ Requires-Python: >=3.11,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: pycountry (>=24.6.1,<25.0.0)
14
+ Requires-Dist: pydantic (>=2.9.2,<3.0.0)
15
+ Requires-Dist: pymongo (>=4.10.1,<5.0.0)
16
+ Description-Content-Type: text/markdown
17
+
18
+ # Chatty Analytics
19
+ Models and custom classes to work across the Chattyverse
20
+
21
+ Chatty Analytics is a proprietary tool developed by Axel Gualda. This software is for internal use only and is not licensed for distribution or use outside of authorized contexts.
22
+
23
+ Copyright (c) 2024 Axel Gualda. All Rights Reserved.
24
+
25
+
26
+ poetry version patch
27
+ poetry build
28
+ poetry publish -r testpypi
29
+ pip cache purge
30
+ pip uninstall letschatty -y
31
+ pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple letschatty
32
+ pytest -v tests/unit/models/messages/test_message_from_db.py
33
+
34
+ pip cache purge
35
+ pip uninstall letschatty -y
36
+ pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple letschatty
37
+ pytest -v tests/unit/models/messages/test_message_from_db.py
@@ -0,0 +1,20 @@
1
+ # Chatty Analytics
2
+ Models and custom classes to work across the Chattyverse
3
+
4
+ Chatty Analytics is a proprietary tool developed by Axel Gualda. This software is for internal use only and is not licensed for distribution or use outside of authorized contexts.
5
+
6
+ Copyright (c) 2024 Axel Gualda. All Rights Reserved.
7
+
8
+
9
+ poetry version patch
10
+ poetry build
11
+ poetry publish -r testpypi
12
+ pip cache purge
13
+ pip uninstall letschatty -y
14
+ pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple letschatty
15
+ pytest -v tests/unit/models/messages/test_message_from_db.py
16
+
17
+ pip cache purge
18
+ pip uninstall letschatty -y
19
+ pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple letschatty
20
+ pytest -v tests/unit/models/messages/test_message_from_db.py
@@ -0,0 +1,19 @@
1
+ [tool.poetry]
2
+ name = "letschatty"
3
+ version = "0.1.44" # Or whichever is your latest version
4
+ description = "Models and custom classes to work across the Chattyverse"
5
+ authors = ["Axel <axel@letschatty.com>"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ packages = [{include = "letschatty", from = "src"}]
9
+
10
+ [tool.poetry.dependencies]
11
+ python = "^3.11"
12
+ pymongo = "^4.10.1"
13
+ pydantic = "^2.9.2"
14
+ pycountry = "^24.6.1"
15
+
16
+
17
+ [build-system]
18
+ requires = ["poetry-core"]
19
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,2 @@
1
+ from .models import *
2
+ from .services import *
@@ -0,0 +1,4 @@
1
+ from .messages import *
2
+ from .events import *
3
+ from .metrics import *
4
+ from .utils import *
@@ -0,0 +1,11 @@
1
+ from pydantic import BaseModel, Field
2
+ from typing import Optional
3
+
4
+ class Empresa(BaseModel):
5
+ name: str
6
+ phone_number_id: str = Field(alias="company_id")
7
+ bussiness_account_id: str
8
+ photo_url: str
9
+ meta_token: str
10
+ slack_channel_id: Optional[str] = Field(default=None)
11
+ phone_numbers_for_testing: list[str] = Field(default=[])
@@ -0,0 +1,6 @@
1
+ from .event_types import EventType
2
+ from .contact_point import ContactPointEvent
3
+ from .new_chat import NewChatEvent
4
+ from .quality_scoring import QualityScoringEvent
5
+ from .sale import SaleEvent
6
+ from .template import TemplateEvent
@@ -0,0 +1,45 @@
1
+ from pydantic import BaseModel, Field
2
+ from enum import StrEnum
3
+ from bson import ObjectId
4
+ from datetime import datetime, timezone
5
+ from typing import Dict, Any
6
+ from .event_types import EventType
7
+ from ..utils.types.identifier import StrObjectId
8
+ import re
9
+
10
+ class ChattyCloudEvent(BaseModel):
11
+ id: StrObjectId = Field(default_factory=lambda: str(ObjectId()), alias="_id")
12
+ source: str = Field(..., pattern=r"com\.chatty/[a-z-]+/[a-zA-Z0-9-]+")
13
+ specversion: str = Field(default="1.0")
14
+ type: EventType
15
+ time: datetime = Field(default_factory=lambda: datetime.now(tz=timezone.utc))
16
+ datacontenttype: str = Field(default="application/json")
17
+ data: Dict[str, Any]
18
+
19
+ class ClientEvent(ChattyCloudEvent):
20
+ """Used for client related events, either management wise like an agent event or a business logic event like a new_chat."""
21
+ source: str = Field(..., pattern=r"com\.chatty/clients/[a-zA-Z0-9-]+")
22
+
23
+ @property
24
+ def client_id(self) -> str:
25
+ match = re.search(r"com\.chatty/clients/([a-zA-Z0-9-]+)", self.source)
26
+ if match:
27
+ return match.group(1)
28
+ raise ValueError("Invalid source format")
29
+
30
+ class InternalEvent(ChattyCloudEvent):
31
+ """Used for internal events such as a new client, some billing, etc."""
32
+ source: str = Field(..., pattern=r"com\.chatty/internal/[a-z-]+")
33
+
34
+ class BusinessLogicEvent(ClientEvent):
35
+ """Used for business logic events, such as a quality scoring."""
36
+ source: str = Field(..., pattern=r"com\.chatty/business-logic/[a-zA-Z0-9-]+")
37
+
38
+ class BusinessLogicEventData(BaseModel):
39
+ channel_id : str
40
+ chat_id : StrObjectId
41
+ client_channel_user_id : str = Field(..., alias="waid")
42
+ company_phone_number_id : str
43
+ subtype : StrEnum
44
+
45
+
@@ -0,0 +1,18 @@
1
+ from .base import BusinessLogicEventData, BusinessLogicEvent
2
+ from .event_types import EventType
3
+ from ..utils.types.identifier import StrObjectId
4
+ from enum import StrEnum
5
+
6
+ class CPSubtype(StrEnum):
7
+ CREATED = "created"
8
+ UPDATED = "updated"
9
+ DELETED = "deleted"
10
+
11
+ class ContactPointData(BusinessLogicEventData):
12
+ source_id: StrObjectId
13
+ new_chat : bool
14
+
15
+ class ContactPointEvent(BusinessLogicEvent):
16
+ """Used for client related events, such as a new chat, a touchpoint, etc."""
17
+ type: EventType = EventType.CONTACT_POINT
18
+ data: ContactPointData
@@ -0,0 +1,11 @@
1
+ from enum import StrEnum
2
+
3
+ class EventType(StrEnum):
4
+ CONTACT_POINT = "contact_point"
5
+ NEW_CHAT = "new_chat"
6
+ TEMPLATE = "template"
7
+ QUALITY_SCORING = "quality_scoring"
8
+ SALE = "sale"
9
+
10
+
11
+
@@ -0,0 +1,11 @@
1
+ from .base import BusinessLogicEventData, BusinessLogicEvent
2
+ from .event_types import EventType
3
+ from enum import StrEnum
4
+
5
+ class NewChatData(BusinessLogicEventData):
6
+ is_inbound : bool
7
+
8
+ class NewChatEvent(BusinessLogicEvent):
9
+ """Used for client related events, such as a new chat, a touchpoint, etc."""
10
+ type: EventType = EventType.NEW_CHAT
11
+ data: NewChatData
@@ -0,0 +1,16 @@
1
+ from .base import BusinessLogicEventData, BusinessLogicEvent
2
+ from .event_types import EventType
3
+ from enum import StrEnum
4
+
5
+ class QualityScore(StrEnum):
6
+ BAD = "bad"
7
+ GOOD = "good"
8
+ NEUTRAL = "neutral"
9
+
10
+ class QualityScoringData(BusinessLogicEventData):
11
+ score : QualityScore
12
+
13
+ class QualityScoringEvent(BusinessLogicEvent):
14
+ """Used for client related events, such as a new chat, a touchpoint, etc."""
15
+ type: EventType = EventType.QUALITY_SCORING
16
+ data: QualityScoringData
@@ -0,0 +1,16 @@
1
+ from .base import BusinessLogicEventData, BusinessLogicEvent
2
+ from .event_types import EventType
3
+ from enum import StrEnum
4
+
5
+ class SaleSubtype(StrEnum):
6
+ CREATED = "created"
7
+ UPDATED = "updated"
8
+ DELETED = "deleted"
9
+
10
+ class SaleData(BusinessLogicEventData):
11
+ sale_id: str
12
+
13
+ class SaleEvent(BusinessLogicEvent):
14
+ """Used for client related events, such as a new chat, a touchpoint, etc."""
15
+ type: EventType = EventType.SALE
16
+ data: SaleData
@@ -0,0 +1,19 @@
1
+ from .base import BusinessLogicEventData, BusinessLogicEvent
2
+ from .event_types import EventType
3
+ from enum import StrEnum
4
+
5
+ class TemplateSubtype(StrEnum):
6
+ WAITING = "waiting"
7
+ SENT = "sent"
8
+ DELIVERED = "delivered"
9
+ FAILED = "failed"
10
+ READ = "read"
11
+
12
+ class TemplateData(BusinessLogicEventData):
13
+ message_id: str
14
+ template_name: str
15
+
16
+ class TemplateEvent(BusinessLogicEvent):
17
+ """Used for client related events, such as a new chat, a touchpoint, etc."""
18
+ type: EventType = EventType.TEMPLATE
19
+ data: TemplateData
@@ -0,0 +1,2 @@
1
+ from .chatty_messages import *
2
+ from .messages_request import *
@@ -0,0 +1,17 @@
1
+ from typing import Union, TypeAlias
2
+ from .audio import AudioMessage
3
+ from .document import DocumentMessage
4
+ from .image import ImageMessage
5
+ from .location import LocationMessage
6
+ from .sticker import StickerMessage
7
+ from .text import TextMessage
8
+ from .video import VideoMessage
9
+ from .unsupported import UnsupportedMessage
10
+ from .reaction import ReactionMessage
11
+ from .central_notification import CentralNotification
12
+ from .contact import ContactMessage
13
+ from .base import ChattyMessageJson
14
+ # from .button import ButtonMessage
15
+
16
+ ChattyMessage : TypeAlias = Union[AudioMessage, DocumentMessage, ImageMessage, LocationMessage, StickerMessage, TextMessage, VideoMessage, UnsupportedMessage, ReactionMessage, CentralNotification, ContactMessage]
17
+ ChattyMediaMessage : TypeAlias = Union[AudioMessage, ImageMessage, VideoMessage, StickerMessage, DocumentMessage]
@@ -0,0 +1,7 @@
1
+ from .base.message_base import Message
2
+ from .schema import ChattyContentAudio
3
+ from ...utils.types.message_types import MessageType
4
+
5
+ class AudioMessage(Message):
6
+ type: MessageType = MessageType.AUDIO
7
+ content: ChattyContentAudio
@@ -0,0 +1 @@
1
+ from .chatty_message_json import ChattyMessageJson
@@ -0,0 +1,22 @@
1
+ from pydantic import BaseModel, Field
2
+ from typing import Optional, Dict, Any
3
+ from datetime import datetime
4
+ from ....utils.types.message_types import MessageType, MessageSubtype
5
+ from ....utils.types.message_status_types import Status
6
+
7
+ class Content(BaseModel):
8
+ pass
9
+
10
+ class ChattyMessageJson(BaseModel):
11
+ """This is the database message model"""
12
+ id: str
13
+ created_at: datetime
14
+ updated_at: datetime
15
+ type: MessageType
16
+ status: Status
17
+ is_incoming_message: bool
18
+ content: Dict[str, Any]
19
+ sent_by: Optional[str] = Field(default=None)
20
+ referral: Optional[Dict] = Field(default=None)
21
+ context: Optional[Dict] = Field(default=None)
22
+ subtype: Optional[str] = Field(default="")
@@ -0,0 +1,117 @@
1
+ from pydantic import BaseModel, Field, field_validator, ValidationInfo
2
+ from datetime import datetime, timezone
3
+ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
4
+ from typing import Dict, Any, Optional
5
+
6
+ from bson import ObjectId
7
+ from ....utils import MessageType, Status, MessageSubtype
8
+ from ..schema import ChattyContent, ChattyContext, ChattyReferral
9
+
10
+ class Message(BaseModel):
11
+ created_at: datetime
12
+ updated_at: datetime
13
+ type: MessageType
14
+ content: ChattyContent
15
+ status: Status
16
+ is_incoming_message: bool
17
+ id: str = Field(default_factory=lambda: str(ObjectId()),description="Unique identifier for the message. In case it's a central notification or a message request (still not confirmed by external API) we'll use an object id as default. Also known as wamid form Meta")
18
+ sent_by: Optional[str] = Field(description="Email of the agent who sent the message. If it's incoming, it'll be None")
19
+ starred: bool = Field(default=False)
20
+ subtype: MessageSubtype = Field(default_factory=lambda: MessageSubtype.NONE)
21
+ referral: ChattyReferral = Field(default_factory=lambda: ChattyReferral.default())
22
+ context: ChattyContext = Field(default_factory=lambda: ChattyContext.default())
23
+
24
+ def model_dump(self, *args, **kwargs) -> Dict[str, Any]:
25
+ """
26
+ Dump the message to a dictionary, with the option to convert datetimes to a specific timezone.
27
+ """
28
+ timezone = kwargs.pop('timezone', None)
29
+ data = super().model_dump(*args, **kwargs)
30
+ if timezone:
31
+ try:
32
+ tz = ZoneInfo(timezone)
33
+ data['created_at'] = self.created_at.astimezone(tz).isoformat()
34
+ data['updated_at'] = self.updated_at.astimezone(tz).isoformat()
35
+ except ZoneInfoNotFoundError:
36
+ raise ValueError(f"Invalid timezone: {timezone}")
37
+ return data
38
+
39
+ class ConfigDict:
40
+ validate_assignment = True
41
+
42
+ @field_validator('sent_by', mode='before')
43
+ def validate_sent_by(cls, v: Optional[str], info: ValidationInfo) -> Optional[str]:
44
+ """Ensure that the sent_by field is not required for incoming messages, and that it's a valid email for outgoing messages."""
45
+ is_incoming = info.data.get('is_incoming_message', False)
46
+ if is_incoming:
47
+ return None
48
+ if not is_incoming and not v:
49
+ raise ValueError("sent_by is required for outgoing messages")
50
+ return v
51
+
52
+ @field_validator('status', mode='before')
53
+ def validate_status(cls, v: Optional[str], info: ValidationInfo) -> Status:
54
+ """Status should be validated always, both instantiation and assignment"""
55
+ if isinstance(v, str):
56
+ return Status(v)
57
+ return v
58
+
59
+ @field_validator('subtype', mode='before')
60
+ def validate_subtype(cls, v: Optional[str], info: ValidationInfo) -> MessageSubtype:
61
+ """If there's no subtype, we'll use the subtype NONE."""
62
+ if v is None or v == "":
63
+ return MessageSubtype.NONE
64
+ return v
65
+
66
+ @field_validator('referral', mode='before')
67
+ def validate_referral(cls, v: Optional[Dict], info: ValidationInfo) -> ChattyReferral:
68
+ """If there's no referral, we'll use the referral default."""
69
+ if v is None or v == {}:
70
+ return ChattyReferral.default()
71
+ return v
72
+
73
+ @field_validator('context', mode='before')
74
+ def validate_context(cls, v: Optional[Dict], info: ValidationInfo) -> ChattyContext:
75
+ """If there's no context, we'll use the context default."""
76
+ if v is None or v == {}:
77
+ return ChattyContext.default()
78
+ return v
79
+
80
+ @field_validator('context', mode='after')
81
+ def validate_context_based_on_subtype(cls, v: ChattyContext, info: ValidationInfo) -> ChattyContext:
82
+ """We'll validate the context based on the subtype.
83
+ And if there's no context, we'll use the context default."""
84
+ subtype = info.data.get('subtype')
85
+ if subtype == MessageSubtype.TEMPLATE:
86
+ if v.template_name is None:
87
+ raise ValueError("template_name is required for template messages")
88
+ elif subtype == MessageSubtype.CHATTY_RESPONSE:
89
+ if v.response_id is None:
90
+ raise ValueError("response_id is required for chatty_response messages")
91
+ return v
92
+
93
+ @field_validator('created_at', 'updated_at')
94
+ def ensure_utc(cls, v):
95
+ if isinstance(v, datetime):
96
+ return v.replace(tzinfo=timezone.utc) if v.tzinfo is None else v.astimezone(timezone.utc)
97
+ raise ValueError('must be a datetime')
98
+
99
+ def _update_status(self, new_status: Status):
100
+ self.status = new_status
101
+ self.updated_at = datetime.now(timezone.utc)
102
+
103
+ def mark_as_read(self):
104
+ """Mark the message as read if not already failed."""
105
+ self._update_status(Status.READ)
106
+
107
+ def mark_as_delivered(self):
108
+ """Mark the message as delivered if it was sent."""
109
+ self._update_status(Status.DELIVERED)
110
+
111
+ def mark_as_failed(self):
112
+ """Mark the message as failed."""
113
+ self._update_status(Status.FAILED)
114
+
115
+ def mark_as_sent(self):
116
+ """Mark the message as sent."""
117
+ self._update_status(Status.SENT)
@@ -0,0 +1,7 @@
1
+ from .base.message_base import Message
2
+ from ...utils.types.message_types import MessageType
3
+
4
+
5
+ class ButtonMessage(Message):
6
+ #TODO: Implementar
7
+ pass
@@ -0,0 +1,8 @@
1
+ from .base.message_base import Message
2
+ from ...utils.types.message_types import MessageType
3
+ from .schema import ChattyContentCentral
4
+
5
+ class CentralNotification(Message):
6
+ type: str = MessageType.CENTRAL
7
+ content: ChattyContentCentral
8
+
@@ -0,0 +1,7 @@
1
+ from .base.message_base import Message
2
+ from ...utils.types.message_types import MessageType
3
+ from .schema import ChattyContentContacts
4
+
5
+ class ContactMessage(Message):
6
+ type: MessageType = MessageType.CONTACT
7
+ content: ChattyContentContacts
@@ -0,0 +1,7 @@
1
+ from .base.message_base import Message
2
+ from ...utils.types.message_types import MessageType
3
+ from .schema import ChattyContentDocument
4
+
5
+ class DocumentMessage(Message):
6
+ type: MessageType = MessageType.DOCUMENT
7
+ content: ChattyContentDocument
@@ -0,0 +1,7 @@
1
+ from .base.message_base import Message
2
+ from ...utils.types.message_types import MessageType
3
+ from .schema import ChattyContentImage
4
+
5
+ class ImageMessage(Message):
6
+ type: MessageType = MessageType.IMAGE
7
+ content: ChattyContentImage
@@ -0,0 +1,6 @@
1
+ from .base.message_base import Message
2
+ from ...utils.types.message_types import MessageType
3
+
4
+ class InteractiveMessage(Message):
5
+ #TODO: Implementar
6
+ pass
@@ -0,0 +1,7 @@
1
+ from .base.message_base import Message
2
+ from ...utils.types.message_types import MessageType
3
+ from .schema import ChattyContentLocation
4
+
5
+ class LocationMessage(Message):
6
+ type: str = MessageType.LOCATION.value
7
+ content: ChattyContentLocation
@@ -0,0 +1,7 @@
1
+ from .base.message_base import Message
2
+ from ...utils.types.message_types import MessageType
3
+ from .schema import ChattyContentReaction
4
+
5
+ class ReactionMessage(Message):
6
+ type: MessageType = MessageType.REACTION
7
+ content: ChattyContentReaction
@@ -0,0 +1,18 @@
1
+ from typing import TypeAlias, Union
2
+ from .chatty_content.content_image import ChattyContentImage
3
+ from .chatty_content.content_text import ChattyContentText
4
+ from .chatty_content.content_video import ChattyContentVideo
5
+ from .chatty_content.content_audio import ChattyContentAudio
6
+ from .chatty_content.content_reaction import ChattyContentReaction
7
+ from .chatty_content.content_location import ChattyContentLocation
8
+ from .chatty_content.content_contacts import ChattyContentContacts
9
+ from .chatty_content.content_document import ChattyContentDocument
10
+ from .chatty_content.content_sticker import ChattyContentSticker
11
+ from .chatty_content.content_central import ChattyContentCentral
12
+
13
+ from .chatty_context.chatty_context import ChattyContext
14
+ from .chatty_referal.chatty_referal import ChattyReferral
15
+
16
+
17
+ ChattyContent : TypeAlias = Union[ChattyContentImage, ChattyContentText, ChattyContentVideo, ChattyContentAudio, ChattyContentReaction, ChattyContentLocation, ChattyContentContacts, ChattyContentDocument, ChattyContentSticker, ChattyContentCentral]
18
+
@@ -0,0 +1,6 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+ class ChattyContentAudio(BaseModel):
4
+ url: str = Field(description="URL of the media from S3")
5
+ mime_type: str
6
+ sha256: str
@@ -0,0 +1,5 @@
1
+ from pydantic import BaseModel
2
+
3
+ # Soolo se generan DESDE LA BASE DE DATOS
4
+ class ChattyContentCentral(BaseModel):
5
+ body: str