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.
@@ -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
+ ]
@@ -0,0 +1,9 @@
1
+ from enum import Enum
2
+
3
+
4
+ class SyncStatusEnum(str, Enum):
5
+ STARTED = "STARTED"
6
+ RUNNING = "RUNNING"
7
+ FINISHED = "FINISHED"
8
+ FAILED = "FAILED"
9
+
@@ -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, ChattyContentText
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 reason:
230
- body = f"{body}. Motivo: {reason}"
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: letschatty
3
- Version: 0.4.347
3
+ Version: 0.4.349
4
4
  Summary: Models and custom classes to work across the Chattyverse
5
5
  License: MIT
6
6
  Author: Axel
@@ -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=N_I-kRMHrtnp00c_bbFSiYYM47HJRS-d5slgtbexsl4,229
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=2INPC8YSZJcT10Gny_zXV_PujeqUGccsZHV31ZSywyw,5961
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=UKmFpLJaoK7SHC2neNgGFnP9PYjUa03H2cVkplfSI9A,50209
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.347.dist-info/LICENSE,sha256=EClLu_bO2HBLDcThowIwfaIg5EOwIYhpRsBJjVEk92A,1197
280
- letschatty-0.4.347.dist-info/METADATA,sha256=XBq4h1YtuUJqunaMbK96HeCgM3ZYMO7WeuRZXugTVYw,3283
281
- letschatty-0.4.347.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
282
- letschatty-0.4.347.dist-info/RECORD,,
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,,