dhisana 0.0.1.dev265__py3-none-any.whl → 0.0.1.dev266__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.
- dhisana/utils/email_provider.py +3 -0
- dhisana/utils/mailgun_tools.py +10 -7
- dhisana/utils/sendgrid_tools.py +12 -8
- {dhisana-0.0.1.dev265.dist-info → dhisana-0.0.1.dev266.dist-info}/METADATA +1 -1
- {dhisana-0.0.1.dev265.dist-info → dhisana-0.0.1.dev266.dist-info}/RECORD +8 -8
- {dhisana-0.0.1.dev265.dist-info → dhisana-0.0.1.dev266.dist-info}/WHEEL +0 -0
- {dhisana-0.0.1.dev265.dist-info → dhisana-0.0.1.dev266.dist-info}/entry_points.txt +0 -0
- {dhisana-0.0.1.dev265.dist-info → dhisana-0.0.1.dev266.dist-info}/top_level.txt +0 -0
dhisana/utils/email_provider.py
CHANGED
|
@@ -124,9 +124,12 @@ async def send_email_async(
|
|
|
124
124
|
|
|
125
125
|
Returns whatever the underlying provider helper returns:
|
|
126
126
|
|
|
127
|
+
* Mailgun → str (message-id from Mailgun)
|
|
128
|
+
* SendGrid → str (X-Message-Id from SendGrid)
|
|
127
129
|
* SMTP → str (Message-ID)
|
|
128
130
|
* Microsoft 365 → str (message-id)
|
|
129
131
|
* Google Workspace → str (message-id)
|
|
132
|
+
* Google OAuth → str (message-id)
|
|
130
133
|
"""
|
|
131
134
|
# ------------------------------------------------------------------ #
|
|
132
135
|
# 1) Try the preferred providers in order
|
dhisana/utils/mailgun_tools.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import asyncio
|
|
2
1
|
import logging
|
|
3
2
|
import os
|
|
4
3
|
from typing import Optional, List, Dict
|
|
@@ -21,7 +20,7 @@ def get_mailgun_notify_key(tool_config: Optional[List[Dict]] = None) -> str:
|
|
|
21
20
|
if tool_config:
|
|
22
21
|
cfg = next((item for item in tool_config if item.get("name") == "mailgun"), None)
|
|
23
22
|
if cfg:
|
|
24
|
-
cfg_map = {i
|
|
23
|
+
cfg_map = {i.get("name"): i.get("value") for i in cfg.get("configuration", []) if i}
|
|
25
24
|
key = cfg_map.get("apiKey")
|
|
26
25
|
key = key or os.getenv("MAILGUN_NOTIFY_KEY")
|
|
27
26
|
if not key:
|
|
@@ -43,7 +42,7 @@ def get_mailgun_notify_domain(tool_config: Optional[List[Dict]] = None) -> str:
|
|
|
43
42
|
if tool_config:
|
|
44
43
|
cfg = next((item for item in tool_config if item.get("name") == "mailgun"), None)
|
|
45
44
|
if cfg:
|
|
46
|
-
cfg_map = {i
|
|
45
|
+
cfg_map = {i.get("name"): i.get("value") for i in cfg.get("configuration", []) if i}
|
|
47
46
|
domain = cfg_map.get("domain") or cfg_map.get("notifyDomain")
|
|
48
47
|
domain = domain or os.getenv("MAILGUN_DOMAIN") or os.getenv("MAILGUN_NOTIFY_DOMAIN")
|
|
49
48
|
if not domain:
|
|
@@ -96,11 +95,15 @@ async def send_email_with_mailgun(
|
|
|
96
95
|
# Try to return JSON payload if available
|
|
97
96
|
try:
|
|
98
97
|
return await response.json()
|
|
99
|
-
except Exception:
|
|
98
|
+
except Exception as parse_ex:
|
|
99
|
+
logging.debug(f"Could not parse Mailgun response as JSON: {parse_ex}")
|
|
100
100
|
return await response.text()
|
|
101
|
-
except
|
|
101
|
+
except (aiohttp.ClientError, ValueError) as ex:
|
|
102
102
|
logging.warning(f"Error sending email via Mailgun: {ex}")
|
|
103
103
|
return {"error": str(ex)}
|
|
104
|
+
except Exception as ex:
|
|
105
|
+
logging.exception(f"Unexpected error sending email via Mailgun: {ex}")
|
|
106
|
+
raise
|
|
104
107
|
|
|
105
108
|
|
|
106
109
|
async def send_email_using_mailgun_async(
|
|
@@ -147,10 +150,10 @@ async def send_email_using_mailgun_async(
|
|
|
147
150
|
raise RuntimeError(f"Mailgun send failed: {detail}")
|
|
148
151
|
try:
|
|
149
152
|
payload = await response.json()
|
|
150
|
-
except Exception:
|
|
153
|
+
except Exception as parse_ex:
|
|
154
|
+
logging.debug(f"Could not parse Mailgun response as JSON: {parse_ex}")
|
|
151
155
|
payload = {"message": await response.text()}
|
|
152
156
|
|
|
153
157
|
# Normalise return value akin to other providers
|
|
154
158
|
msg_id = payload.get("id") if isinstance(payload, dict) else None
|
|
155
|
-
await asyncio.sleep(20)
|
|
156
159
|
return msg_id or str(payload)
|
dhisana/utils/sendgrid_tools.py
CHANGED
|
@@ -119,13 +119,17 @@ async def send_email_with_sendgrid(
|
|
|
119
119
|
headers=headers,
|
|
120
120
|
json=payload,
|
|
121
121
|
) as response:
|
|
122
|
-
# SendGrid returns 202 Accepted on success with empty body
|
|
122
|
+
# SendGrid returns 202 Accepted on success with empty body but includes X-Message-Id header
|
|
123
123
|
if response.status == 202:
|
|
124
|
-
|
|
124
|
+
message_id = response.headers.get("X-Message-Id")
|
|
125
|
+
if not message_id:
|
|
126
|
+
logging.warning("SendGrid did not return X-Message-Id header")
|
|
127
|
+
return {"status": 202, "message": "accepted", "message_id": message_id}
|
|
125
128
|
# On error, try to parse JSON for helpful message
|
|
126
129
|
try:
|
|
127
130
|
err = await response.json()
|
|
128
|
-
except Exception:
|
|
131
|
+
except Exception as parse_ex:
|
|
132
|
+
logging.debug(f"Could not parse SendGrid error as JSON: {parse_ex}")
|
|
129
133
|
err = {"text": await response.text()}
|
|
130
134
|
return {"error": err, "status": response.status}
|
|
131
135
|
except Exception as ex:
|
|
@@ -139,12 +143,8 @@ async def send_email_using_sendgrid_async(
|
|
|
139
143
|
) -> str:
|
|
140
144
|
"""
|
|
141
145
|
Provider-style wrapper for SendGrid using SendEmailContext.
|
|
142
|
-
Returns
|
|
146
|
+
Returns the message ID from SendGrid's X-Message-Id response header.
|
|
143
147
|
"""
|
|
144
|
-
plain_body, html_body, _ = body_variants(
|
|
145
|
-
ctx.body,
|
|
146
|
-
getattr(ctx, "body_format", None),
|
|
147
|
-
)
|
|
148
148
|
result = await send_email_with_sendgrid(
|
|
149
149
|
sender=f"{ctx.sender_name} <{ctx.sender_email}>",
|
|
150
150
|
recipients=[ctx.recipient],
|
|
@@ -156,6 +156,10 @@ async def send_email_using_sendgrid_async(
|
|
|
156
156
|
)
|
|
157
157
|
# Normalise output to a string id-like value
|
|
158
158
|
if isinstance(result, dict) and result.get("status") == 202:
|
|
159
|
+
message_id = result.get("message_id")
|
|
160
|
+
if message_id:
|
|
161
|
+
return message_id
|
|
162
|
+
# Fallback if header wasn't present (shouldn't happen)
|
|
159
163
|
return f"sent:{ctx.sender_email}:{ctx.recipient}:{ctx.subject}"
|
|
160
164
|
if isinstance(result, dict) and "error" in result:
|
|
161
165
|
raise RuntimeError(f"SendGrid send failed: {result['error']}")
|
|
@@ -31,7 +31,7 @@ dhisana/utils/dataframe_tools.py,sha256=R6eUXjwR5SG6_K87rWjj4T5PT2w6xvVF2EKBajIv
|
|
|
31
31
|
dhisana/utils/domain_parser.py,sha256=Kw5MPP06wK2azWQzuSiOE-DffOezLqDyF-L9JEBsMSU,1206
|
|
32
32
|
dhisana/utils/email_body_utils.py,sha256=rlCVjdBlqNnEiUberJGXGcrYY1GQOkW0-aB6AEpS3L4,2302
|
|
33
33
|
dhisana/utils/email_parse_helpers.py,sha256=LIdm1B1IyGSW50y8EkxOk6YRjvxO2SJTgTKPLxYls_o,4613
|
|
34
|
-
dhisana/utils/email_provider.py,sha256=
|
|
34
|
+
dhisana/utils/email_provider.py,sha256=ukW_0nHcjTQmpnE9pdJci78LrZcsK1_0v6kcgc2ChPY,14573
|
|
35
35
|
dhisana/utils/enrich_lead_information.py,sha256=O0fV-8MlXFT_z5aXvmvXVT76AISN94GpvAOlq3q_Phw,39411
|
|
36
36
|
dhisana/utils/extract_email_content_for_llm.py,sha256=SQmMZ3YJtm3ZI44XiWEVAItcAwrsSSy1QzDne7LTu_Q,3713
|
|
37
37
|
dhisana/utils/fetch_openai_config.py,sha256=LjWdFuUeTNeAW106pb7DLXZNElos2PlmXRe6bHZJ2hw,5159
|
|
@@ -54,7 +54,7 @@ dhisana/utils/hubspot_crm_tools.py,sha256=lbXFCeq690_TDLjDG8Gm5E-2f1P5EuDqNf5j8P
|
|
|
54
54
|
dhisana/utils/instantly_tools.py,sha256=hhqjDPyLE6o0dzzuvryszbK3ipnoGU2eBm6NlsUGJjY,4771
|
|
55
55
|
dhisana/utils/linkedin_crawler.py,sha256=6fMQTY5lTw2kc65SFHgOAM6YfezAS0Yhg-jkiX8LGHo,6533
|
|
56
56
|
dhisana/utils/lusha_tools.py,sha256=MdiWlxBBjSNpSKz8rhNOyLPtbeh-YWHgGiUq54vN_gM,12734
|
|
57
|
-
dhisana/utils/mailgun_tools.py,sha256=
|
|
57
|
+
dhisana/utils/mailgun_tools.py,sha256=brOgfEx-ciqEdDkXEfzMBfXkG0kRWVscg76tQDXb_lk,5826
|
|
58
58
|
dhisana/utils/mailreach_tools.py,sha256=uJ_gIcg8qrj5-k3jnYYhpwLVnQncoA1swzr5Jfkc1JU,3864
|
|
59
59
|
dhisana/utils/microsoft365_tools.py,sha256=ClqBzTrJ2SZM5K9nsOFyyHRfV-d-6jlxXNpNONtgLlY,18596
|
|
60
60
|
dhisana/utils/openai_assistant_and_file_utils.py,sha256=-eyPcxFvtS-DDtYQGle1SU6C6CuxjulVIojFy27HeWc,8957
|
|
@@ -70,7 +70,7 @@ dhisana/utils/sales_navigator_crawler.py,sha256=z8yurwUTLXdM71xWPDSAFNuDyA_SlanT
|
|
|
70
70
|
dhisana/utils/salesforce_crm_tools.py,sha256=r6tROej4PtfcRN2AViPD7tV24oxBNm6QCE7uwhDH5Hc,17169
|
|
71
71
|
dhisana/utils/search_router.py,sha256=p_1MPHbjalBM8gZuU4LADbmqSLNtZ4zll6CbPOc0POU,4610
|
|
72
72
|
dhisana/utils/search_router_jobs.py,sha256=LgCHNGLMSv-ovgzF32muprfaDTdTpIKgrP5F7swAqhk,1721
|
|
73
|
-
dhisana/utils/sendgrid_tools.py,sha256=
|
|
73
|
+
dhisana/utils/sendgrid_tools.py,sha256=0aafzCxcmCtKVt7kWYNTH_Np9KF0-RPqAZ9LsslMlqs,5931
|
|
74
74
|
dhisana/utils/serarch_router_local_business.py,sha256=n9yZjeXKOSgBnr0lCSQomP1nN3ucbC9ZTTSmSHQLeVo,2920
|
|
75
75
|
dhisana/utils/serpapi_additional_tools.py,sha256=Xb1tc_oK-IjI9ZrEruYhFg8UJMLHQDaO9B51YiNbeBs,10569
|
|
76
76
|
dhisana/utils/serpapi_google_jobs.py,sha256=HUJFZEW8UvYqsW0sWlEDXgI_IUomh5fTkzRJzEgsDGc,4509
|
|
@@ -95,8 +95,8 @@ dhisana/workflow/agent.py,sha256=esv7_i_XuMkV2j1nz_UlsHov_m6X5WZZiZm_tG4OBHU,565
|
|
|
95
95
|
dhisana/workflow/flow.py,sha256=xWE3qQbM7j2B3FH8XnY3zOL_QXX4LbTW4ArndnEYJE0,1638
|
|
96
96
|
dhisana/workflow/task.py,sha256=HlWz9mtrwLYByoSnePOemBUBrMEcj7KbgNjEE1oF5wo,1830
|
|
97
97
|
dhisana/workflow/test.py,sha256=E7lRnXK0PguTNzyasHytLzTJdkqIPxG5_4qk4hMEeKc,3399
|
|
98
|
-
dhisana-0.0.1.
|
|
99
|
-
dhisana-0.0.1.
|
|
100
|
-
dhisana-0.0.1.
|
|
101
|
-
dhisana-0.0.1.
|
|
102
|
-
dhisana-0.0.1.
|
|
98
|
+
dhisana-0.0.1.dev266.dist-info/METADATA,sha256=KEVAlP8-K5O412xaBb3WeJWM6S1thO8dRYiEu3PQKvw,1190
|
|
99
|
+
dhisana-0.0.1.dev266.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
100
|
+
dhisana-0.0.1.dev266.dist-info/entry_points.txt,sha256=jujxteZmNI9EkEaK-pOCoWuBujU8TCevdkfl9ZcKHek,49
|
|
101
|
+
dhisana-0.0.1.dev266.dist-info/top_level.txt,sha256=NETTHt6YifG_P7XtRHbQiXZlgSFk9Qh9aR-ng1XTf4s,8
|
|
102
|
+
dhisana-0.0.1.dev266.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|