letschatty 0.4.347__py3-none-any.whl → 0.4.349__py3-none-any.whl
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.
- letschatty/models/company/__init__.py +6 -1
- letschatty/models/company/empresa.py +2 -1
- letschatty/models/company/integrations/product_sync_status.py +28 -0
- letschatty/models/company/integrations/shopify/company_shopify_integration.py +62 -0
- letschatty/models/company/integrations/shopify/shopify_product_sync_status.py +18 -0
- letschatty/models/company/integrations/shopify/shopify_webhook_topics.py +40 -0
- letschatty/models/company/integrations/sync_status_enum.py +9 -0
- letschatty/services/chat/chat_service.py +4 -23
- {letschatty-0.4.347.dist-info → letschatty-0.4.349.dist-info}/METADATA +1 -1
- {letschatty-0.4.347.dist-info → letschatty-0.4.349.dist-info}/RECORD +12 -7
- {letschatty-0.4.347.dist-info → letschatty-0.4.349.dist-info}/LICENSE +0 -0
- {letschatty-0.4.347.dist-info → letschatty-0.4.349.dist-info}/WHEEL +0 -0
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
from .assets import *
|
|
2
2
|
from .empresa import EmpresaModel
|
|
3
3
|
from .form_field import FormField, FormFieldPreview, CollectedData, SystemFormFields
|
|
4
|
-
from .CRM.funnel import Funnel, FunnelPreview, ChatFunnel, StageTransition, FunnelStatus
|
|
4
|
+
from .CRM.funnel import Funnel, FunnelPreview, ChatFunnel, StageTransition, FunnelStatus
|
|
5
|
+
from .integrations.product_sync_status import ProductSyncStatus
|
|
6
|
+
from .integrations.sync_status_enum import SyncStatusEnum
|
|
7
|
+
from .integrations.shopify.shopify_webhook_topics import ShopifyWebhookTopic
|
|
8
|
+
from .integrations.shopify.company_shopify_integration import ShopifyIntegration
|
|
9
|
+
from .integrations.shopify.shopify_product_sync_status import ShopifyProductSyncStatus, ShopifyProductSyncStatusEnum
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from letschatty.models.company.integrations.shopify.company_shopify_integration import ShopifyIntegration
|
|
1
2
|
from pydantic import Field, ConfigDict, field_validator, SecretStr, model_validator
|
|
2
3
|
from typing import Optional, List, Dict
|
|
3
4
|
|
|
@@ -34,7 +35,7 @@ class EmpresaModel(ChattyAssetModel):
|
|
|
34
35
|
continuous_conversation_template_name: Optional[str] = Field(default = None, description="The name of the continuous conversation template")
|
|
35
36
|
default_follow_up_strategy_id: Optional[StrObjectId] = Field(default = None, description="The id of the default follow up strategy")
|
|
36
37
|
messaging_settings: MessagingSettings = Field(default = MessagingSettings(), description="The messaging settings for the company")
|
|
37
|
-
|
|
38
|
+
shopify_integration: ShopifyIntegration = Field(default = ShopifyIntegration(), description="The Shopify integration for the company")
|
|
38
39
|
|
|
39
40
|
model_config = ConfigDict(
|
|
40
41
|
validate_by_name=True,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import ClassVar, Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from letschatty.models.company.integrations.sync_status_enum import SyncStatusEnum
|
|
9
|
+
from letschatty.models.base_models import CompanyAssetModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ProductSyncStatus(CompanyAssetModel):
|
|
13
|
+
"""Generic product sync status for any e-commerce integration."""
|
|
14
|
+
|
|
15
|
+
COLLECTION: ClassVar[str] = "product_sync_statuses"
|
|
16
|
+
|
|
17
|
+
integration_type: str = Field(
|
|
18
|
+
description="Integration type (shopify, tiendanube, etc.)"
|
|
19
|
+
)
|
|
20
|
+
status: SyncStatusEnum = Field(description="Current sync status")
|
|
21
|
+
|
|
22
|
+
products_created: int = Field(default=0)
|
|
23
|
+
products_updated: int = Field(default=0)
|
|
24
|
+
|
|
25
|
+
name: str = Field(default="")
|
|
26
|
+
|
|
27
|
+
finished_at: Optional[datetime] = Field(default=None)
|
|
28
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from zoneinfo import ZoneInfo
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
class ShopifyWebhookSubscription(BaseModel):
|
|
7
|
+
"""Represents a single webhook subscription"""
|
|
8
|
+
topic: str = Field(description="Webhook topic (e.g., 'products/create')")
|
|
9
|
+
webhook_id: Optional[str] = Field(default=None, description="Shopify webhook ID")
|
|
10
|
+
subscribed_at: datetime = Field(default_factory=lambda: datetime.now(tz=ZoneInfo("UTC")))
|
|
11
|
+
is_active: bool = Field(default=True, description="Whether subscription is active")
|
|
12
|
+
|
|
13
|
+
class ShopifyIntegration(BaseModel):
|
|
14
|
+
"""Shopify integration for the company"""
|
|
15
|
+
shopify_store_url: str = Field(default="")
|
|
16
|
+
oauth_state: str = Field(default="")
|
|
17
|
+
oauth_state_at: datetime = Field(default_factory=lambda: datetime.now(tz=ZoneInfo("UTC")))
|
|
18
|
+
access_token: Optional[str] = Field(default=None)
|
|
19
|
+
connected_at: Optional[datetime] = Field(default=None)
|
|
20
|
+
scope: Optional[str] = Field(default=None)
|
|
21
|
+
|
|
22
|
+
# Webhook subscriptions
|
|
23
|
+
webhook_subscriptions: List[ShopifyWebhookSubscription] = Field(
|
|
24
|
+
default_factory=list,
|
|
25
|
+
description="List of active webhook subscriptions"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Scheduled sync settings
|
|
29
|
+
product_sync_enabled: bool = Field(
|
|
30
|
+
default=False,
|
|
31
|
+
description="Whether scheduled product sync is enabled"
|
|
32
|
+
)
|
|
33
|
+
product_sync_interval_hours: int = Field(
|
|
34
|
+
default=24,
|
|
35
|
+
description="Interval in hours for scheduled product sync"
|
|
36
|
+
)
|
|
37
|
+
last_product_sync_at: Optional[datetime] = Field(
|
|
38
|
+
default=None,
|
|
39
|
+
description="Timestamp of last product sync"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def is_connected(self) -> bool:
|
|
44
|
+
"""Check if the integration is fully connected"""
|
|
45
|
+
return bool(self.access_token and self.shopify_store_url)
|
|
46
|
+
|
|
47
|
+
def get_subscribed_topics(self) -> List[str]:
|
|
48
|
+
"""Get list of currently subscribed webhook topics"""
|
|
49
|
+
return [sub.topic for sub in self.webhook_subscriptions if sub.is_active]
|
|
50
|
+
|
|
51
|
+
def reset(self) -> None:
|
|
52
|
+
"""Reset integration to disconnected state"""
|
|
53
|
+
self.shopify_store_url = ""
|
|
54
|
+
self.oauth_state = ""
|
|
55
|
+
self.oauth_state_at = datetime.now(tz=ZoneInfo("UTC"))
|
|
56
|
+
self.access_token = None
|
|
57
|
+
self.connected_at = None
|
|
58
|
+
self.scope = None
|
|
59
|
+
self.webhook_subscriptions = []
|
|
60
|
+
self.product_sync_enabled = False
|
|
61
|
+
self.product_sync_interval_hours = 24
|
|
62
|
+
self.last_product_sync_at = None
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from letschatty.models.company.integrations.product_sync_status import ProductSyncStatus
|
|
4
|
+
from letschatty.models.company.integrations.sync_status_enum import SyncStatusEnum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Backwards-compatible alias (Shopify-specific name, generic enum)
|
|
8
|
+
ShopifyProductSyncStatusEnum = SyncStatusEnum
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ShopifyProductSyncStatus(ProductSyncStatus):
|
|
12
|
+
"""Shopify-flavored wrapper for the generic ProductSyncStatus."""
|
|
13
|
+
|
|
14
|
+
integration_type: str = Field(
|
|
15
|
+
default="shopify",
|
|
16
|
+
frozen=True,
|
|
17
|
+
description="Integration type for this sync status"
|
|
18
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
class ShopifyWebhookTopic(str, Enum):
|
|
4
|
+
"""Shopify webhook topics for products and orders"""
|
|
5
|
+
|
|
6
|
+
# Product webhooks
|
|
7
|
+
PRODUCTS_CREATE = "products/create"
|
|
8
|
+
PRODUCTS_UPDATE = "products/update"
|
|
9
|
+
PRODUCTS_DELETE = "products/delete"
|
|
10
|
+
|
|
11
|
+
# Order webhooks
|
|
12
|
+
ORDERS_CREATE = "orders/create"
|
|
13
|
+
ORDERS_UPDATE = "orders/updated"
|
|
14
|
+
ORDERS_DELETE = "orders/delete"
|
|
15
|
+
ORDERS_FULFILLED = "orders/fulfilled"
|
|
16
|
+
ORDERS_PARTIALLY_FULFILLED = "orders/partially_fulfilled"
|
|
17
|
+
ORDERS_PAID = "orders/paid"
|
|
18
|
+
ORDERS_CANCELLED = "orders/cancelled"
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def get_product_topics(cls) -> list[str]:
|
|
22
|
+
"""Get all product-related webhook topics"""
|
|
23
|
+
return [
|
|
24
|
+
cls.PRODUCTS_CREATE.value,
|
|
25
|
+
cls.PRODUCTS_UPDATE.value,
|
|
26
|
+
cls.PRODUCTS_DELETE.value,
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_order_topics(cls) -> list[str]:
|
|
31
|
+
"""Get all order-related webhook topics"""
|
|
32
|
+
return [
|
|
33
|
+
cls.ORDERS_CREATE.value,
|
|
34
|
+
cls.ORDERS_UPDATE.value,
|
|
35
|
+
cls.ORDERS_DELETE.value,
|
|
36
|
+
cls.ORDERS_FULFILLED.value,
|
|
37
|
+
cls.ORDERS_PARTIALLY_FULFILLED.value,
|
|
38
|
+
cls.ORDERS_PAID.value,
|
|
39
|
+
cls.ORDERS_CANCELLED.value,
|
|
40
|
+
]
|
|
@@ -28,10 +28,9 @@ from ...models.chat.scheduled_messages import ScheduledMessageStatus
|
|
|
28
28
|
from ...models.utils.types.identifier import StrObjectId
|
|
29
29
|
from ...models.utils.custom_exceptions.custom_exceptions import AssetAlreadyAssigned, MessageNotFoundError, NotFoundError, MessageAlreadyInChat, MetaErrorNotification, ChatAlreadyAssigned, AlreadyCompleted, ErrorToMantainSafety
|
|
30
30
|
from ..factories.messages.central_notification_factory import CentralNotificationFactory
|
|
31
|
-
from ..factories.messages.chatty_message_factory import from_message_draft
|
|
32
31
|
from ...models.messages.chatty_messages.base.message_draft import ChattyContentAudio, MessageDraft
|
|
33
32
|
from ...models.messages.chatty_messages.schema.chatty_content.content_central import CentralNotificationStatus
|
|
34
|
-
from ...models.messages.chatty_messages.schema import ChattyContext
|
|
33
|
+
from ...models.messages.chatty_messages.schema import ChattyContext
|
|
35
34
|
from ...models.utils.types.message_types import MessageType
|
|
36
35
|
from .conversation_topics_service import ConversationTopicsService
|
|
37
36
|
import logging
|
|
@@ -213,12 +212,7 @@ class ChatService:
|
|
|
213
212
|
return chat.chatty_ai_agent
|
|
214
213
|
|
|
215
214
|
@staticmethod
|
|
216
|
-
def escalate_chatty_ai_agent(
|
|
217
|
-
chat: Chat,
|
|
218
|
-
execution_context: ExecutionContext,
|
|
219
|
-
message: Optional[str] = None,
|
|
220
|
-
reason: Optional[str] = None
|
|
221
|
-
) -> None:
|
|
215
|
+
def escalate_chatty_ai_agent(chat: Chat, execution_context: ExecutionContext, message: Optional[str] = None) -> None:
|
|
222
216
|
"""
|
|
223
217
|
Mark the chat's AI agent as requiring human intervention and add a central notification.
|
|
224
218
|
"""
|
|
@@ -226,8 +220,8 @@ class ChatService:
|
|
|
226
220
|
chat.chatty_ai_agent.requires_human_intervention = True
|
|
227
221
|
execution_context.set_event_time(datetime.now(tz=ZoneInfo("UTC")))
|
|
228
222
|
body = "El chat fue escalado a un agente humano"
|
|
229
|
-
if
|
|
230
|
-
body = f"{body}. Motivo: {
|
|
223
|
+
if message:
|
|
224
|
+
body = f"{body}. Motivo: {message}"
|
|
231
225
|
ChatService.add_central_notification_from_text(
|
|
232
226
|
chat=chat,
|
|
233
227
|
body=body,
|
|
@@ -235,19 +229,6 @@ class ChatService:
|
|
|
235
229
|
content_status=CentralNotificationStatus.WARNING,
|
|
236
230
|
context=ChattyContext(chain_of_thought_id=execution_context.chain_of_thought_id)
|
|
237
231
|
)
|
|
238
|
-
if message:
|
|
239
|
-
outgoing_context = ChattyContext(chain_of_thought_id=execution_context.chain_of_thought_id)
|
|
240
|
-
outgoing_message = from_message_draft(
|
|
241
|
-
MessageDraft(
|
|
242
|
-
type=MessageType.TEXT,
|
|
243
|
-
content=ChattyContentText(body=message),
|
|
244
|
-
context=outgoing_context,
|
|
245
|
-
subtype=MessageSubtype.NONE,
|
|
246
|
-
is_incoming_message=False
|
|
247
|
-
),
|
|
248
|
-
sent_by=execution_context.executor.id
|
|
249
|
-
)
|
|
250
|
-
ChatService.add_message(chat=chat, message=outgoing_message)
|
|
251
232
|
|
|
252
233
|
@staticmethod
|
|
253
234
|
def add_workflow_link(chat : Chat, link : LinkItem, flow:FlowPreview, execution_context: ExecutionContext, description: str, last_incoming_message_id: Optional[str] = None, next_call: Optional[datetime] = None) -> FlowStateAssignedToChat:
|
|
@@ -79,7 +79,7 @@ letschatty/models/chat/temporary_chat.py,sha256=MR68vYrNvoyf40cLs2p5NnI8BNeN1VGT
|
|
|
79
79
|
letschatty/models/chat/time_left.py,sha256=2uA9dS_0QAVnWn7Q2wIwGe4tMrlYBP9qySC7M8I0jb8,3307
|
|
80
80
|
letschatty/models/company/CRM/business_area.py,sha256=U0F_7Rbq-e5tMSj_MugPqMHm2-IHxbAKbN0PWZ_93-o,296
|
|
81
81
|
letschatty/models/company/CRM/funnel.py,sha256=gBGZhud060sK5QFI5y9NgmFuhLCOAtzK81TKBhK2gxE,15820
|
|
82
|
-
letschatty/models/company/__init__.py,sha256=
|
|
82
|
+
letschatty/models/company/__init__.py,sha256=s6maWpREH-RAPoTm8svdivJ5foNEBkb5K6ncyZ0oEjo,626
|
|
83
83
|
letschatty/models/company/assets/__init__.py,sha256=z0xN_lt1D76bXt8DXVdbH3GkofUPQPZU9NioRSLt_lI,244
|
|
84
84
|
letschatty/models/company/assets/ai_agents_v2/ai_agent_message_draft.py,sha256=xbshA34RSjHm2g8J7hW2FkWo-Qm8MH2HTbcRcoYmyvc,2076
|
|
85
85
|
letschatty/models/company/assets/ai_agents_v2/ai_agents_decision_output.py,sha256=aEKZCOsGiFFPSx23fkS5Khfsxo-r8JGk3O0sxiGs8T0,5876
|
|
@@ -126,9 +126,14 @@ letschatty/models/company/assets/workflow_execution.py,sha256=CKlT9JTryKC8kNniRX
|
|
|
126
126
|
letschatty/models/company/company_chats_snapshot.py,sha256=Mg9Wmu3pfE-t5Sf57FCj-psvc5TkDkFfma-hdh6nE0Q,668
|
|
127
127
|
letschatty/models/company/company_messaging_settgins.py,sha256=7isXt7gmqw-FKSUSZwdctdXDFMfPi2OyPuWqB7WpC6c,957
|
|
128
128
|
letschatty/models/company/conversation_topic.py,sha256=__IinJ3YcUsiApF4Em5uAgd01ydRUrlCVxaat-pCCRg,1704
|
|
129
|
-
letschatty/models/company/empresa.py,sha256=
|
|
129
|
+
letschatty/models/company/empresa.py,sha256=o3IgQqMjQmyDjmR9je3bSF4EiIlJk7BnafiCO5c3pVE,6205
|
|
130
130
|
letschatty/models/company/form_field.py,sha256=LGyMLybwvfEPG3hMmwsoP_4wnMaPJbVFpG6c1HdVC8E,9802
|
|
131
131
|
letschatty/models/company/insight.py,sha256=B7BL07E4Z1b9aJHi3PXC1atln4-7fSo9JeqgQoeB_ao,7459
|
|
132
|
+
letschatty/models/company/integrations/product_sync_status.py,sha256=115zOvkaeXpHIUQftXe6dJsOUki3ru8hG3c75e2v4ag,814
|
|
133
|
+
letschatty/models/company/integrations/shopify/company_shopify_integration.py,sha256=PbxgIOG0Qp9Bz0WDrQffuSWrbEiV93JjX7PtHeYsqa0,2496
|
|
134
|
+
letschatty/models/company/integrations/shopify/shopify_product_sync_status.py,sha256=FhsmtFIf82pO9a1_OwV3MyKiJ6CGzlw-SCANZoV2M2g,588
|
|
135
|
+
letschatty/models/company/integrations/shopify/shopify_webhook_topics.py,sha256=HlSm0JHH6G3NPug8tbx9PQaC9yO3cwXJPTuU3qkWdDk,1263
|
|
136
|
+
letschatty/models/company/integrations/sync_status_enum.py,sha256=gh5kkKgLRqhygFFUPhITksp0Z-ImFEpGpzVABr7JiFM,154
|
|
132
137
|
letschatty/models/company/notifications/notification.py,sha256=wE7rIi21nZno6jjIxajMz4e7OJbzrDHjH1KdkNzJiF8,1907
|
|
133
138
|
letschatty/models/copilot/links.py,sha256=mcddNR6WdWOoOr3NgDl_FElxF15SiZZXw9wmIV08HRw,185
|
|
134
139
|
letschatty/models/data_base/__init__.py,sha256=cUTj-TBUYJn1iAofztCr3VUJFF9qg1vmaQlOuoKb1MM,45
|
|
@@ -231,7 +236,7 @@ letschatty/services/ai_agents/tool_descriptions/human_handover.json,sha256=-b3l_
|
|
|
231
236
|
letschatty/services/ai_agents/tool_descriptions.py,sha256=MMFhcgEl6Z7k8BUv3Yx1pCxHzIjc8HD93S0uM5OPj8Y,105
|
|
232
237
|
letschatty/services/ai_components_service.py,sha256=QUb-NdzHj4nPmnceVSQ9Pe3swFWUrkUFKzDZeuP0b5o,3762
|
|
233
238
|
letschatty/services/chat/chat_extractors.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
234
|
-
letschatty/services/chat/chat_service.py,sha256=
|
|
239
|
+
letschatty/services/chat/chat_service.py,sha256=75ebdL9C1k4NU_zfTsGqSi31H0Gv1yqB-IOHJK6GyQY,49439
|
|
235
240
|
letschatty/services/chat/client_service.py,sha256=TtlG0GJDqx6YemfueiJzAtMNk_udnPhg0MJG0bKwnk8,1097
|
|
236
241
|
letschatty/services/chat/conversation_topics_service.py,sha256=jvIkpHTCz0JKXui3n_BRs6UQ-TB3x7DrQsnX2zUVFVw,2461
|
|
237
242
|
letschatty/services/chatty_assets/__init__.py,sha256=quOXMGvcDVRkbeaGfCQliFfZzis5m1wrfJkHqDkWRx8,132
|
|
@@ -276,7 +281,7 @@ letschatty/services/template_campaigns/template_campaign_service.py,sha256=jORgD
|
|
|
276
281
|
letschatty/services/users/agent_service.py,sha256=hIkUUJ1SpkKbh5_uo4i2CeqGtuMTjU7tSV8k5J7WPG4,279
|
|
277
282
|
letschatty/services/users/user_factory.py,sha256=FCB9uiAfjMeYfh4kMdx5h8VDHJ8MCsD-uaxW3X3KaWM,6681
|
|
278
283
|
letschatty/services/validators/analytics_validator.py,sha256=6ejecLcif2i1C5trUo1qQgp8vKr9WchdljFZ5GzB2i4,7239
|
|
279
|
-
letschatty-0.4.
|
|
280
|
-
letschatty-0.4.
|
|
281
|
-
letschatty-0.4.
|
|
282
|
-
letschatty-0.4.
|
|
284
|
+
letschatty-0.4.349.dist-info/LICENSE,sha256=EClLu_bO2HBLDcThowIwfaIg5EOwIYhpRsBJjVEk92A,1197
|
|
285
|
+
letschatty-0.4.349.dist-info/METADATA,sha256=ATZLw89l0YE2PLTXbysezhxCDad8Epk_7yCeReWIzUA,3283
|
|
286
|
+
letschatty-0.4.349.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
287
|
+
letschatty-0.4.349.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|