agentle 0.9.39__py3-none-any.whl → 0.9.41__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.
- agentle/agents/whatsapp/models/whatsapp_bot_config.py +13 -1
- agentle/agents/whatsapp/models/whatsapp_webhook_payload.py +59 -8
- agentle/agents/whatsapp/providers/evolution/evolution_api_provider.py +14 -5
- agentle/agents/whatsapp/whatsapp_bot.py +102 -8
- {agentle-0.9.39.dist-info → agentle-0.9.41.dist-info}/METADATA +1 -1
- {agentle-0.9.39.dist-info → agentle-0.9.41.dist-info}/RECORD +8 -8
- {agentle-0.9.39.dist-info → agentle-0.9.41.dist-info}/WHEEL +1 -1
- {agentle-0.9.39.dist-info → agentle-0.9.41.dist-info}/licenses/LICENSE +0 -0
|
@@ -98,7 +98,13 @@ class WhatsAppBotConfig(BaseModel):
|
|
|
98
98
|
description="Default error message",
|
|
99
99
|
)
|
|
100
100
|
welcome_message: str | None = Field(
|
|
101
|
-
default=None, description="Message to send on first interaction"
|
|
101
|
+
default=None, description="Message to send on first interaction (or caption if welcome image is set)"
|
|
102
|
+
)
|
|
103
|
+
welcome_image_url: str | None = Field(
|
|
104
|
+
default=None, description="URL of welcome image to send on first interaction"
|
|
105
|
+
)
|
|
106
|
+
welcome_image_base64: str | None = Field(
|
|
107
|
+
default=None, description="Base64-encoded welcome image to send on first interaction (alternative to welcome_image_url)"
|
|
102
108
|
)
|
|
103
109
|
|
|
104
110
|
# === Message Batching (Simplified) ===
|
|
@@ -257,6 +263,8 @@ class WhatsAppBotConfig(BaseModel):
|
|
|
257
263
|
max_split_messages: int | None = None,
|
|
258
264
|
error_message: str | None = None,
|
|
259
265
|
welcome_message: str | None = None,
|
|
266
|
+
welcome_image_url: str | None = None,
|
|
267
|
+
welcome_image_base64: str | None = None,
|
|
260
268
|
# Message Batching
|
|
261
269
|
enable_message_batching: bool | None = None,
|
|
262
270
|
batch_delay_seconds: float | None = None,
|
|
@@ -366,6 +374,10 @@ class WhatsAppBotConfig(BaseModel):
|
|
|
366
374
|
overrides["error_message"] = error_message
|
|
367
375
|
if welcome_message is not None:
|
|
368
376
|
overrides["welcome_message"] = welcome_message
|
|
377
|
+
if welcome_image_url is not None:
|
|
378
|
+
overrides["welcome_image_url"] = welcome_image_url
|
|
379
|
+
if welcome_image_base64 is not None:
|
|
380
|
+
overrides["welcome_image_base64"] = welcome_image_base64
|
|
369
381
|
|
|
370
382
|
# Message Batching
|
|
371
383
|
if enable_message_batching is not None:
|
|
@@ -77,18 +77,69 @@ class WhatsAppWebhookPayload(BaseModel):
|
|
|
77
77
|
ForwardedFrom: str | None = Field(default=None) # For forwarded messages
|
|
78
78
|
FrequentlyForwarded: str | None = Field(default=None) # "true" or "false" as string
|
|
79
79
|
|
|
80
|
+
# Root level fallbacks for robust extraction
|
|
81
|
+
remoteJid: str | None = Field(default=None)
|
|
82
|
+
remoteJidAlt: str | None = Field(default=None)
|
|
83
|
+
|
|
80
84
|
def model_post_init(self, context: Any, /) -> None:
|
|
81
|
-
if self.phone_number_id
|
|
85
|
+
if self.phone_number_id:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
# Initialize data if missing but root fields are present
|
|
89
|
+
if not self.data and (self.remoteJid or self.remoteJidAlt):
|
|
90
|
+
# This is a bit of a hack to ensure we have a structure to hold the key
|
|
91
|
+
# if we only got root level JIDs
|
|
92
|
+
from agentle.agents.whatsapp.models.key import Key
|
|
93
|
+
from agentle.agents.whatsapp.models.data import Data
|
|
94
|
+
|
|
95
|
+
self.data = Data(
|
|
96
|
+
key=Key(
|
|
97
|
+
remoteJid=self.remoteJid or self.remoteJidAlt or "", fromMe=False
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if not self.data:
|
|
82
102
|
return
|
|
83
103
|
|
|
84
104
|
key = self.data.key
|
|
85
|
-
if "@lid" in key.remoteJid:
|
|
86
|
-
remote_jid_alt = key.remoteJidAlt
|
|
87
|
-
if remote_jid_alt is None:
|
|
88
|
-
raise ValueError("No remotejidalt was provided.")
|
|
89
105
|
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
# Extraction logic:
|
|
107
|
+
# 1. key.remoteJid
|
|
108
|
+
# 2. remoteJid (root)
|
|
109
|
+
# 3. key.remoteJidAlt
|
|
110
|
+
# 4. remoteJidAlt (root)
|
|
111
|
+
|
|
112
|
+
candidates = [
|
|
113
|
+
key.remoteJid,
|
|
114
|
+
self.remoteJid,
|
|
115
|
+
key.remoteJidAlt,
|
|
116
|
+
self.remoteJidAlt,
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
# Find first valid candidate
|
|
120
|
+
selected_jid = next((c for c in candidates if c), None)
|
|
121
|
+
|
|
122
|
+
if not selected_jid:
|
|
123
|
+
# Should we raise? For now let's just return and let validation fail if strict
|
|
92
124
|
return
|
|
125
|
+
|
|
126
|
+
# Optimization: treating @lid
|
|
127
|
+
# If the selected JID is an LID, verify if we have an ALT available
|
|
128
|
+
if "@lid" in selected_jid:
|
|
129
|
+
# If we selected a main JID that is LID, try to find an Alt
|
|
130
|
+
# candidates for alt: key.remoteJidAlt, self.remoteJidAlt
|
|
131
|
+
alt_candidates = [key.remoteJidAlt, self.remoteJidAlt]
|
|
132
|
+
alt_jid = next((c for c in alt_candidates if c), None)
|
|
133
|
+
|
|
134
|
+
if alt_jid:
|
|
135
|
+
selected_jid = alt_jid
|
|
136
|
+
elif not key.remoteJidAlt and not self.remoteJidAlt:
|
|
137
|
+
# If we have an LID but no ALT, we might be in trouble depending on the use case,
|
|
138
|
+
# but we proceed with what we have or raise as before
|
|
139
|
+
# The original code raised ValueError here
|
|
140
|
+
pass
|
|
93
141
|
|
|
94
|
-
self.phone_number_id =
|
|
142
|
+
self.phone_number_id = selected_jid.split("@")[0]
|
|
143
|
+
|
|
144
|
+
# Normalize the key inside data so the rest of the app uses the "best" JID
|
|
145
|
+
self.data.key.remoteJid = selected_jid
|
|
@@ -1597,15 +1597,18 @@ class EvolutionAPIProvider(WhatsAppProvider):
|
|
|
1597
1597
|
Normalize phone number to Evolution API format.
|
|
1598
1598
|
|
|
1599
1599
|
Evolution API expects phone numbers in the format: countrycode+number@s.whatsapp.net
|
|
1600
|
+
For Brazilian numbers, we now test with @lid format instead.
|
|
1600
1601
|
|
|
1601
1602
|
Brazilian mobile format: 55 + DDD (2 digits) + 9 + 8 digits = 13 digits total
|
|
1602
|
-
Example: 5534998137839@
|
|
1603
|
+
Example: 5534998137839@lid
|
|
1603
1604
|
"""
|
|
1604
1605
|
original_phone = phone
|
|
1605
1606
|
|
|
1606
|
-
# Remove @s.whatsapp.net suffix if present
|
|
1607
|
+
# Remove @s.whatsapp.net or @lid suffix if present
|
|
1607
1608
|
if "@s.whatsapp.net" in phone:
|
|
1608
1609
|
phone = phone.split("@")[0]
|
|
1610
|
+
elif "@lid" in phone:
|
|
1611
|
+
phone = phone.split("@")[0]
|
|
1609
1612
|
|
|
1610
1613
|
# Remove non-numeric characters
|
|
1611
1614
|
phone = "".join(c for c in phone if c.isdigit())
|
|
@@ -1657,9 +1660,15 @@ class EvolutionAPIProvider(WhatsAppProvider):
|
|
|
1657
1660
|
f"Phone number may be invalid - 5th digit is not '9': {phone}"
|
|
1658
1661
|
)
|
|
1659
1662
|
|
|
1660
|
-
#
|
|
1661
|
-
if not phone.endswith("@s.whatsapp.net"):
|
|
1662
|
-
|
|
1663
|
+
# TESTING: Use @lid for Brazilian numbers (country code 55) instead of @s.whatsapp.net
|
|
1664
|
+
if not phone.endswith("@lid") and not phone.endswith("@s.whatsapp.net"):
|
|
1665
|
+
if phone.startswith("55"):
|
|
1666
|
+
# Brazilian number - use @lid
|
|
1667
|
+
phone = phone + "@lid"
|
|
1668
|
+
logger.info(f"🧪 TESTING: Using @lid for Brazilian number: {phone}")
|
|
1669
|
+
else:
|
|
1670
|
+
# Non-Brazilian number - use @s.whatsapp.net
|
|
1671
|
+
phone = phone + "@s.whatsapp.net"
|
|
1663
1672
|
|
|
1664
1673
|
if original_phone != phone:
|
|
1665
1674
|
logger.info(f"Phone number normalized: {original_phone} -> {phone}")
|
|
@@ -431,23 +431,117 @@ class WhatsAppBot[T_Schema: WhatsAppResponseBase = WhatsAppResponseBase](BaseMod
|
|
|
431
431
|
f"[MESSAGE_HANDLER] Effective chat_id para conversação: {effective_chat_id}"
|
|
432
432
|
)
|
|
433
433
|
|
|
434
|
-
# Check welcome message for first interaction
|
|
434
|
+
# Check welcome message/image for first interaction
|
|
435
435
|
if (
|
|
436
436
|
await cast(
|
|
437
437
|
ConversationStore, self.agent.conversation_store
|
|
438
438
|
).get_conversation_history_length(effective_chat_id)
|
|
439
439
|
== 0
|
|
440
|
-
and
|
|
440
|
+
and (
|
|
441
|
+
self.config.welcome_message
|
|
442
|
+
or self.config.welcome_image_url
|
|
443
|
+
or self.config.welcome_image_base64
|
|
444
|
+
)
|
|
441
445
|
):
|
|
442
446
|
logger.info(
|
|
443
|
-
f"[WELCOME] Sending welcome message to {message.from_number}"
|
|
447
|
+
f"[WELCOME] Sending welcome message/image to {message.from_number}"
|
|
444
448
|
)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
message.from_number, formatted_welcome
|
|
449
|
+
|
|
450
|
+
# Determine if we're sending an image or text
|
|
451
|
+
has_welcome_image = (
|
|
452
|
+
self.config.welcome_image_url or self.config.welcome_image_base64
|
|
450
453
|
)
|
|
454
|
+
|
|
455
|
+
if has_welcome_image:
|
|
456
|
+
# Prepare caption from welcome_message if available
|
|
457
|
+
caption = None
|
|
458
|
+
if self.config.welcome_message:
|
|
459
|
+
caption = self._format_whatsapp_markdown(
|
|
460
|
+
self.config.welcome_message
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
# Handle welcome image via URL
|
|
464
|
+
if self.config.welcome_image_url:
|
|
465
|
+
logger.info(
|
|
466
|
+
f"[WELCOME] Sending welcome image from URL to {message.from_number}"
|
|
467
|
+
)
|
|
468
|
+
await self.provider.send_media_message(
|
|
469
|
+
to=message.from_number,
|
|
470
|
+
media_url=self.config.welcome_image_url,
|
|
471
|
+
media_type="image",
|
|
472
|
+
caption=caption,
|
|
473
|
+
)
|
|
474
|
+
# Handle welcome image via base64
|
|
475
|
+
elif self.config.welcome_image_base64:
|
|
476
|
+
# Try to upload base64 to file storage if available
|
|
477
|
+
image_url = None
|
|
478
|
+
if self.file_storage_manager:
|
|
479
|
+
try:
|
|
480
|
+
import base64
|
|
481
|
+
import time
|
|
482
|
+
|
|
483
|
+
logger.info(
|
|
484
|
+
"[WELCOME] Uploading base64 welcome image to file storage"
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
# Decode base64 to bytes
|
|
488
|
+
image_bytes = base64.b64decode(
|
|
489
|
+
self.config.welcome_image_base64
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
# Generate unique filename
|
|
493
|
+
timestamp = int(time.time())
|
|
494
|
+
filename = f"welcome_image_{timestamp}.jpg"
|
|
495
|
+
|
|
496
|
+
# Upload to storage
|
|
497
|
+
image_url = await self.file_storage_manager.upload_file(
|
|
498
|
+
file_data=image_bytes,
|
|
499
|
+
filename=filename,
|
|
500
|
+
mime_type="image/jpeg",
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
logger.info(
|
|
504
|
+
f"[WELCOME] Welcome image uploaded to storage: {image_url}"
|
|
505
|
+
)
|
|
506
|
+
except Exception as e:
|
|
507
|
+
logger.warning(
|
|
508
|
+
f"[WELCOME] Failed to upload welcome image to storage: {e}"
|
|
509
|
+
)
|
|
510
|
+
image_url = None
|
|
511
|
+
|
|
512
|
+
# Send image via URL if upload succeeded, otherwise send text fallback
|
|
513
|
+
if image_url:
|
|
514
|
+
logger.info(
|
|
515
|
+
f"[WELCOME] Sending uploaded welcome image to {message.from_number}"
|
|
516
|
+
)
|
|
517
|
+
await self.provider.send_media_message(
|
|
518
|
+
to=message.from_number,
|
|
519
|
+
media_url=image_url,
|
|
520
|
+
media_type="image",
|
|
521
|
+
caption=caption,
|
|
522
|
+
)
|
|
523
|
+
else:
|
|
524
|
+
# Fallback to text if base64 upload failed and no URL available
|
|
525
|
+
logger.warning(
|
|
526
|
+
"[WELCOME] Could not send welcome image (base64 upload failed and no URL), falling back to text"
|
|
527
|
+
)
|
|
528
|
+
if caption:
|
|
529
|
+
await self.provider.send_text_message(
|
|
530
|
+
message.from_number, caption
|
|
531
|
+
)
|
|
532
|
+
else:
|
|
533
|
+
logger.warning(
|
|
534
|
+
"[WELCOME] No caption available, skipping welcome message"
|
|
535
|
+
)
|
|
536
|
+
else:
|
|
537
|
+
# Send text-only welcome message (backward compatible)
|
|
538
|
+
formatted_welcome = self._format_whatsapp_markdown(
|
|
539
|
+
self.config.welcome_message
|
|
540
|
+
)
|
|
541
|
+
await self.provider.send_text_message(
|
|
542
|
+
message.from_number, formatted_welcome
|
|
543
|
+
)
|
|
544
|
+
|
|
451
545
|
session.message_count += 1
|
|
452
546
|
await self.provider.update_session(session)
|
|
453
547
|
|
|
@@ -137,7 +137,7 @@ agentle/agents/ui/__init__.py,sha256=IjHRV0k2DNwvFrEHebmsXiBvmITE8nQUnsR07h9tVkU
|
|
|
137
137
|
agentle/agents/ui/streamlit.py,sha256=9afICL0cxtG1o2pWh6vH39-NdKiVfADKiXo405F2aB0,42829
|
|
138
138
|
agentle/agents/whatsapp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
139
139
|
agentle/agents/whatsapp/human_delay_calculator.py,sha256=BGCDeoNTPsMn4d_QYmG0BWGCG8SiUJC6Fk295ulAsAk,18268
|
|
140
|
-
agentle/agents/whatsapp/whatsapp_bot.py,sha256=
|
|
140
|
+
agentle/agents/whatsapp/whatsapp_bot.py,sha256=o3fa5iSD-26CcmcMw0pzU0aS9ju1Mm9vD9ZHWwfEI9o,169964
|
|
141
141
|
agentle/agents/whatsapp/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
142
142
|
agentle/agents/whatsapp/models/audio_message.py,sha256=af2apMWzxKcCtXfQN6U2qfOFoiwRj0nCUrKmrBD0whE,3067
|
|
143
143
|
agentle/agents/whatsapp/models/context_info.py,sha256=sk80KuNE36S6VRnLh7n6UXmzZCXIB4E4lNxnRyVizg8,563
|
|
@@ -152,7 +152,7 @@ agentle/agents/whatsapp/models/message_context_info.py,sha256=msCSuu8uMN3G9GDaXd
|
|
|
152
152
|
agentle/agents/whatsapp/models/quoted_message.py,sha256=QC4sp7eLPE9g9i-_f3avb0sDO7gKpkzZR2qkbxqptts,1073
|
|
153
153
|
agentle/agents/whatsapp/models/video_message.py,sha256=0s4ak68euff25a_tXvYscKCFn7rQj8Rj6U89rupQnO0,3697
|
|
154
154
|
agentle/agents/whatsapp/models/whatsapp_audio_message.py,sha256=AAcnjzJC1O5VjyWZaSWpG_tmZFc2-CdcPn9abjyLrpc,378
|
|
155
|
-
agentle/agents/whatsapp/models/whatsapp_bot_config.py,sha256
|
|
155
|
+
agentle/agents/whatsapp/models/whatsapp_bot_config.py,sha256=-YLnzFOUlNwNvROTOlJ1W8RLNwnJ3irXI3znaMo7UnI,37831
|
|
156
156
|
agentle/agents/whatsapp/models/whatsapp_contact.py,sha256=6iO6xmFs7z9hd1N9kZzGyNHYvCaUoCHn3Yi1DAJN4YU,240
|
|
157
157
|
agentle/agents/whatsapp/models/whatsapp_document_message.py,sha256=ECM_hXF-3IbC9itbtZI0eA_XRNXFVefw9Mr-Lo_lrH0,323
|
|
158
158
|
agentle/agents/whatsapp/models/whatsapp_image_message.py,sha256=xOAPRRSgqj9gQ2ZZOGdFWfOgtmNpE1W8mIUAmB5YTpo,314
|
|
@@ -165,13 +165,13 @@ agentle/agents/whatsapp/models/whatsapp_response_base.py,sha256=IIDONx9Ipt593tAZ
|
|
|
165
165
|
agentle/agents/whatsapp/models/whatsapp_session.py,sha256=9G1HC-A2G9jTdpwYy3w9bnYkOGK2vvA7kdYAf32oWMU,15640
|
|
166
166
|
agentle/agents/whatsapp/models/whatsapp_text_message.py,sha256=GpSwFrPC4qpQlVCWKKgYjQJKNv0qvwgYfuoD3ttLzdQ,441
|
|
167
167
|
agentle/agents/whatsapp/models/whatsapp_video_message.py,sha256=-d-4hnkkxyLVNoje3a1pOEAvzWqoCLFcBn70wUpnyXY,346
|
|
168
|
-
agentle/agents/whatsapp/models/whatsapp_webhook_payload.py,sha256=
|
|
168
|
+
agentle/agents/whatsapp/models/whatsapp_webhook_payload.py,sha256=qtLlJJ1RxHkj89bU9tFHplv9qcV_5SqIsp1GOzQFCEU,6207
|
|
169
169
|
agentle/agents/whatsapp/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
170
170
|
agentle/agents/whatsapp/providers/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
171
171
|
agentle/agents/whatsapp/providers/base/whatsapp_provider.py,sha256=Iaywrv0xer4fhZprMttC-NP4-rRYdU_45UzIZQ7dkYA,5349
|
|
172
172
|
agentle/agents/whatsapp/providers/evolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
173
173
|
agentle/agents/whatsapp/providers/evolution/evolution_api_config.py,sha256=mc3jVJ1olnFgt7jP6P_eygvUVhN3XYSAYp1ozIxtAsc,1288
|
|
174
|
-
agentle/agents/whatsapp/providers/evolution/evolution_api_provider.py,sha256=
|
|
174
|
+
agentle/agents/whatsapp/providers/evolution/evolution_api_provider.py,sha256=6nIItw8dwBEuWDa8lb3qaEthN4ywbSRJgYyY49EXowI,69043
|
|
175
175
|
agentle/agents/whatsapp/providers/meta/__init__.py,sha256=ArZ2y9qUALahP2-c0j0ESFKmRjDHiZIurqxYC7MTWA8,1038
|
|
176
176
|
agentle/agents/whatsapp/providers/meta/meta_whatsapp_config.py,sha256=ECzb76Sba0ExrO4NAB7v9HLlgAxsjyTg57mewdVt8EY,1257
|
|
177
177
|
agentle/agents/whatsapp/providers/meta/meta_whatsapp_provider.py,sha256=95xvVrqp6f5Ku49fqOEHUhJUIJPGb1rRvCntoIY2JSM,35953
|
|
@@ -1018,7 +1018,7 @@ agentle/web/actions/scroll.py,sha256=WqVVAORNDK3BL1oASZBPmXJYeSVkPgAOmWA8ibYO82I
|
|
|
1018
1018
|
agentle/web/actions/viewport.py,sha256=KCwm88Pri19Qc6GLHC69HsRxmdJz1gEEAODfggC_fHo,287
|
|
1019
1019
|
agentle/web/actions/wait.py,sha256=IKEywjf-KC4ni9Gkkv4wgc7bY-hk7HwD4F-OFWlyf2w,571
|
|
1020
1020
|
agentle/web/actions/write_text.py,sha256=9mxfHcpKs_L7BsDnJvOYHQwG8M0GWe61SRJAsKk3xQ8,748
|
|
1021
|
-
agentle-0.9.
|
|
1022
|
-
agentle-0.9.
|
|
1023
|
-
agentle-0.9.
|
|
1024
|
-
agentle-0.9.
|
|
1021
|
+
agentle-0.9.41.dist-info/METADATA,sha256=AYTLK3-J2KRaGK_AwjNjyzxsUlgOJciq4GxvsPgW2wI,86879
|
|
1022
|
+
agentle-0.9.41.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
1023
|
+
agentle-0.9.41.dist-info/licenses/LICENSE,sha256=T90S9vqRS6qP-voULxAcvwEs558wRRo6dHuZrjgcOUI,1085
|
|
1024
|
+
agentle-0.9.41.dist-info/RECORD,,
|
|
File without changes
|