dhisana 0.0.1.dev28__tar.gz → 0.0.1.dev29__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.
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/PKG-INFO +1 -1
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/setup.py +1 -1
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/google_workspace_tools.py +59 -67
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana.egg-info/PKG-INFO +1 -1
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/README.md +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/pyproject.toml +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/setup.cfg +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/__init__.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/cli/__init__.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/cli/cli.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/cli/datasets.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/cli/models.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/cli/predictions.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/schemas/__init__.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/schemas/common.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/schemas/sales.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/ui/__init__.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/ui/components.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/__init__.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/agent_task.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/agent_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/apollo_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/assistant_tool_tag.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/built_with_api_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/cache_output_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/check_email_validity_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/check_for_intent_signal.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/clay_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/company_utils.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/compose_cadence.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/compose_salesnav_query.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/compose_search_query.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/compose_three_step_workflow.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/compose_workflow.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/composite_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/create_list_from_sales_navigator.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/create_smart_list.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/dataframe_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/domain_parser.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/enrich_lead_information.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/g2_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_content.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_email.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_email_response.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_flow.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_linkedin_connect_message.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_linkedin_response_message.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_structured_output_internal.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/google_custom_search.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/hubspot_clearbit.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/hubspot_crm_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/instantly_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/linkedin_crawler.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/lusha_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/openai_assistant_and_file_utils.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/openai_helpers.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/proxy_curl_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/python_function_to_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/research_lead.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/sales_navigator_crawler.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/sendgrid_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/serpapi_search_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/trasform_json.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/web_download_parse_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/workflow_code_model.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/zoominfo_tools.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/workflow/__init__.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/workflow/agent.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/workflow/flow.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/workflow/task.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/workflow/test.py +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana.egg-info/SOURCES.txt +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana.egg-info/dependency_links.txt +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana.egg-info/entry_points.txt +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana.egg-info/requires.txt +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana.egg-info/top_level.txt +0 -0
- {dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/tests/test_agent_tools.py +0 -0
|
@@ -15,6 +15,7 @@ from googleapiclient.discovery import build
|
|
|
15
15
|
from googleapiclient.http import MediaIoBaseDownload, MediaFileUpload
|
|
16
16
|
from google.auth.transport.requests import Request
|
|
17
17
|
from googleapiclient.errors import HttpError
|
|
18
|
+
from pydantic import BaseModel
|
|
18
19
|
|
|
19
20
|
from dhisana.utils.assistant_tool_tag import assistant_tool
|
|
20
21
|
|
|
@@ -332,12 +333,16 @@ async def list_files_in_drive_folder_by_name(
|
|
|
332
333
|
# GMAIL EMAIL OPERATIONS
|
|
333
334
|
################################################################################
|
|
334
335
|
|
|
336
|
+
class SendEmailContext(BaseModel):
|
|
337
|
+
recipient: str
|
|
338
|
+
subject: str
|
|
339
|
+
body: str
|
|
340
|
+
sender_name: str
|
|
341
|
+
sender_email: str
|
|
342
|
+
|
|
335
343
|
@assistant_tool
|
|
336
344
|
async def send_email_using_service_account_async(
|
|
337
|
-
|
|
338
|
-
subject: str,
|
|
339
|
-
body: str,
|
|
340
|
-
sender_email: str,
|
|
345
|
+
send_email_context: SendEmailContext,
|
|
341
346
|
tool_config: Optional[List[Dict]] = None
|
|
342
347
|
) -> str:
|
|
343
348
|
"""
|
|
@@ -345,36 +350,27 @@ async def send_email_using_service_account_async(
|
|
|
345
350
|
The service account must have domain-wide delegation to impersonate the sender_email.
|
|
346
351
|
|
|
347
352
|
Args:
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
body (str): The body text of the email.
|
|
351
|
-
sender_email (str): The address to impersonate for sending.
|
|
352
|
-
tool_config (Optional[List[Dict]]): Tool configuration for credentials.
|
|
353
|
+
send_email_context (SendEmailContext): The context with recipient, subject, body, sender_name, and sender_email.
|
|
354
|
+
tool_config (Optional[List[Dict]]): Tool configuration for credentials (if any).
|
|
353
355
|
|
|
354
356
|
Returns:
|
|
355
357
|
str: The ID of the sent message.
|
|
356
|
-
|
|
357
|
-
Raises:
|
|
358
|
-
httpx.HTTPError, Google-related errors for any issues with the API.
|
|
359
358
|
"""
|
|
360
|
-
if not sender_email:
|
|
359
|
+
if not send_email_context.sender_email:
|
|
361
360
|
raise ValueError("sender_email is required to impersonate for sending.")
|
|
362
361
|
|
|
363
362
|
SCOPES = ['https://mail.google.com/']
|
|
364
|
-
credentials = get_google_credentials(sender_email, SCOPES, tool_config)
|
|
363
|
+
credentials = get_google_credentials(send_email_context.sender_email, SCOPES, tool_config)
|
|
365
364
|
access_token = credentials.token
|
|
366
365
|
|
|
367
366
|
gmail_api_url = 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send'
|
|
368
367
|
|
|
369
|
-
|
|
370
|
-
message =
|
|
371
|
-
message['
|
|
372
|
-
message['
|
|
373
|
-
message['subject'] = subject
|
|
368
|
+
message = MIMEText(send_email_context.body)
|
|
369
|
+
message['to'] = send_email_context.recipient
|
|
370
|
+
message['from'] = f"{send_email_context.sender_name} <{send_email_context.sender_email}>"
|
|
371
|
+
message['subject'] = send_email_context.subject
|
|
374
372
|
|
|
375
|
-
# Encode the message in base64url format
|
|
376
373
|
raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
|
|
377
|
-
|
|
378
374
|
payload = {'raw': raw_message}
|
|
379
375
|
headers = {
|
|
380
376
|
'Authorization': f'Bearer {access_token}',
|
|
@@ -389,13 +385,16 @@ async def send_email_using_service_account_async(
|
|
|
389
385
|
return sent_message.get('id', 'No ID returned')
|
|
390
386
|
|
|
391
387
|
|
|
388
|
+
class QueryEmailContext(BaseModel):
|
|
389
|
+
start_time: str
|
|
390
|
+
end_time: str
|
|
391
|
+
sender_email: str
|
|
392
|
+
unread_only: bool = True
|
|
393
|
+
labels: Optional[List[str]] = None
|
|
394
|
+
|
|
392
395
|
@assistant_tool
|
|
393
396
|
async def list_emails_in_time_range_async(
|
|
394
|
-
|
|
395
|
-
end_time: str,
|
|
396
|
-
sender_email: str,
|
|
397
|
-
unread_only: bool = True,
|
|
398
|
-
labels: Optional[List[str]] = None,
|
|
397
|
+
context: QueryEmailContext,
|
|
399
398
|
tool_config: Optional[List[Dict]] = None
|
|
400
399
|
) -> List[Dict[str, Any]]:
|
|
401
400
|
"""
|
|
@@ -403,43 +402,36 @@ async def list_emails_in_time_range_async(
|
|
|
403
402
|
The service account must have domain-wide delegation to impersonate the sender_email.
|
|
404
403
|
|
|
405
404
|
Args:
|
|
406
|
-
|
|
407
|
-
end_time (str): The end time in RFC 3339 format (e.g., '2021-01-31T23:59:59Z').
|
|
408
|
-
sender_email (str): The mailbox email to impersonate.
|
|
409
|
-
unread_only (bool): If True, only return unread emails.
|
|
410
|
-
labels (Optional[List[str]]): Optional list of Gmail labels to filter.
|
|
405
|
+
context (QueryEmailContext): The query context with start_time, end_time, sender_email, unread_only, and labels.
|
|
411
406
|
tool_config (Optional[List[Dict]]): Tool configuration for credentials.
|
|
412
407
|
|
|
413
408
|
Returns:
|
|
414
409
|
List[Dict[str, Any]]: A list of email message details.
|
|
415
|
-
|
|
416
|
-
Raises:
|
|
417
|
-
httpx.HTTPError, Google-related errors for any issues with the API.
|
|
418
410
|
"""
|
|
419
|
-
if labels is None:
|
|
420
|
-
labels = []
|
|
411
|
+
if context.labels is None:
|
|
412
|
+
context.labels = []
|
|
421
413
|
|
|
422
|
-
if not sender_email:
|
|
414
|
+
if not context.sender_email:
|
|
423
415
|
raise ValueError("sender_email is required to impersonate for listing emails.")
|
|
424
416
|
|
|
425
417
|
SCOPES = ['https://mail.google.com/']
|
|
426
|
-
credentials = get_google_credentials(sender_email, SCOPES, tool_config)
|
|
418
|
+
credentials = get_google_credentials(context.sender_email, SCOPES, tool_config)
|
|
427
419
|
access_token = credentials.token
|
|
428
420
|
|
|
429
421
|
gmail_api_url = 'https://gmail.googleapis.com/gmail/v1/users/me/messages'
|
|
430
422
|
|
|
431
423
|
# Convert RFC 3339 times to Unix epoch timestamps
|
|
432
|
-
start_dt = datetime.datetime.fromisoformat(start_time.replace('Z', '+00:00'))
|
|
433
|
-
end_dt = datetime.datetime.fromisoformat(end_time.replace('Z', '+00:00'))
|
|
424
|
+
start_dt = datetime.datetime.fromisoformat(context.start_time.replace('Z', '+00:00'))
|
|
425
|
+
end_dt = datetime.datetime.fromisoformat(context.end_time.replace('Z', '+00:00'))
|
|
434
426
|
start_timestamp = int(start_dt.timestamp())
|
|
435
427
|
end_timestamp = int(end_dt.timestamp())
|
|
436
428
|
|
|
437
429
|
# Build the search query
|
|
438
430
|
query = f'after:{start_timestamp} before:{end_timestamp}'
|
|
439
|
-
if unread_only:
|
|
431
|
+
if context.unread_only:
|
|
440
432
|
query += ' is:unread'
|
|
441
|
-
if labels:
|
|
442
|
-
label_query = ' '.join([f'label:{lbl}' for lbl in labels])
|
|
433
|
+
if context.labels:
|
|
434
|
+
label_query = ' '.join([f'label:{lbl}' for lbl in context.labels])
|
|
443
435
|
query += f' {label_query}'
|
|
444
436
|
|
|
445
437
|
headers = {'Authorization': f'Bearer {access_token}'}
|
|
@@ -458,7 +450,6 @@ async def list_emails_in_time_range_async(
|
|
|
458
450
|
message_response.raise_for_status()
|
|
459
451
|
message_data = message_response.json()
|
|
460
452
|
|
|
461
|
-
# Extract relevant info
|
|
462
453
|
headers_list = message_data['payload']['headers']
|
|
463
454
|
email_details.append({
|
|
464
455
|
"mailbox_email_id": message_data['id'],
|
|
@@ -639,13 +630,17 @@ async def get_email_details_async(
|
|
|
639
630
|
return message_details
|
|
640
631
|
|
|
641
632
|
|
|
633
|
+
class ReplyEmailContext(BaseModel):
|
|
634
|
+
message_id: str
|
|
635
|
+
reply_body: str
|
|
636
|
+
sender_email: str
|
|
637
|
+
sender_name: str
|
|
638
|
+
mark_as_read: str = "True"
|
|
639
|
+
add_labels: Optional[List[str]] = None
|
|
640
|
+
|
|
642
641
|
@assistant_tool
|
|
643
642
|
async def reply_to_email_async(
|
|
644
|
-
|
|
645
|
-
reply_body: str,
|
|
646
|
-
sender_email: str,
|
|
647
|
-
mark_as_read: str = "True",
|
|
648
|
-
add_labels: Optional[List[str]] = None,
|
|
643
|
+
reply_email_context: ReplyEmailContext,
|
|
649
644
|
tool_config: Optional[List[Dict]] = None
|
|
650
645
|
) -> Dict[str, Any]:
|
|
651
646
|
"""
|
|
@@ -653,35 +648,32 @@ async def reply_to_email_async(
|
|
|
653
648
|
The service account must have domain-wide delegation to impersonate the sender_email.
|
|
654
649
|
|
|
655
650
|
Args:
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
sender_email (str): The mailbox email to impersonate for domain-wide delegation.
|
|
659
|
-
mark_as_read (str): If "True", mark the email thread as read after replying.
|
|
660
|
-
add_labels (List[str]): Optional list of labels to add to the email thread.
|
|
651
|
+
context (ReplyEmailContext): The context with message_id, reply_body, sender_email, sender_name,
|
|
652
|
+
mark_as_read, and add_labels.
|
|
661
653
|
tool_config (Optional[List[Dict]]): Tool configuration for credentials.
|
|
662
654
|
|
|
663
655
|
Returns:
|
|
664
656
|
Dict[str, Any]: A dictionary containing the details of the sent message.
|
|
665
657
|
"""
|
|
666
|
-
if add_labels is None:
|
|
667
|
-
add_labels = []
|
|
658
|
+
if reply_email_context.add_labels is None:
|
|
659
|
+
reply_email_context.add_labels = []
|
|
668
660
|
|
|
669
|
-
if not sender_email:
|
|
661
|
+
if not reply_email_context.sender_email:
|
|
670
662
|
raise ValueError("sender_email is required to impersonate for replying to an email.")
|
|
671
663
|
|
|
672
664
|
SCOPES = ['https://mail.google.com/']
|
|
673
|
-
credentials = get_google_credentials(sender_email, SCOPES, tool_config)
|
|
665
|
+
credentials = get_google_credentials(reply_email_context.sender_email, SCOPES, tool_config)
|
|
674
666
|
access_token = credentials.token
|
|
675
667
|
|
|
676
|
-
# 1. Retrieve original message
|
|
677
668
|
gmail_api_base_url = 'https://gmail.googleapis.com/gmail/v1/users/me'
|
|
678
|
-
get_message_url = f'{gmail_api_base_url}/messages/{message_id}'
|
|
669
|
+
get_message_url = f'{gmail_api_base_url}/messages/{reply_email_context.message_id}'
|
|
679
670
|
headers = {
|
|
680
671
|
'Authorization': f'Bearer {access_token}',
|
|
681
672
|
'Content-Type': 'application/json'
|
|
682
673
|
}
|
|
683
674
|
params = {'format': 'full'}
|
|
684
675
|
|
|
676
|
+
# 1. Retrieve original message
|
|
685
677
|
async with httpx.AsyncClient() as client:
|
|
686
678
|
response = await client.get(get_message_url, headers=headers, params=params)
|
|
687
679
|
response.raise_for_status()
|
|
@@ -701,11 +693,11 @@ async def reply_to_email_async(
|
|
|
701
693
|
message_id_header = headers_dict.get('Message-ID', '')
|
|
702
694
|
|
|
703
695
|
# 3. Create the reply email message
|
|
704
|
-
msg = MIMEText(reply_body)
|
|
696
|
+
msg = MIMEText(reply_email_context.reply_body)
|
|
705
697
|
msg['To'] = to_addresses
|
|
706
698
|
if cc_addresses:
|
|
707
699
|
msg['Cc'] = cc_addresses
|
|
708
|
-
msg['From'] = sender_email
|
|
700
|
+
msg['From'] = f"{reply_email_context.sender_name} <{reply_email_context.sender_email}>"
|
|
709
701
|
msg['Subject'] = subject
|
|
710
702
|
msg['In-Reply-To'] = message_id_header
|
|
711
703
|
msg['References'] = message_id_header
|
|
@@ -724,7 +716,7 @@ async def reply_to_email_async(
|
|
|
724
716
|
sent_message = response.json()
|
|
725
717
|
|
|
726
718
|
# 5. (Optional) Mark the thread as read
|
|
727
|
-
if mark_as_read.lower() == "true":
|
|
719
|
+
if reply_email_context.mark_as_read.lower() == "true":
|
|
728
720
|
modify_thread_url = f'{gmail_api_base_url}/threads/{thread_id}/modify'
|
|
729
721
|
modify_payload = {'removeLabelIds': ['UNREAD']}
|
|
730
722
|
async with httpx.AsyncClient() as client:
|
|
@@ -732,9 +724,9 @@ async def reply_to_email_async(
|
|
|
732
724
|
response.raise_for_status()
|
|
733
725
|
|
|
734
726
|
# 6. (Optional) Add labels
|
|
735
|
-
if add_labels:
|
|
727
|
+
if reply_email_context.add_labels:
|
|
736
728
|
modify_thread_url = f'{gmail_api_base_url}/threads/{thread_id}/modify'
|
|
737
|
-
modify_payload = {'addLabelIds': add_labels}
|
|
729
|
+
modify_payload = {'addLabelIds': reply_email_context.add_labels}
|
|
738
730
|
async with httpx.AsyncClient() as client:
|
|
739
731
|
response = await client.post(modify_thread_url, headers=headers, json=modify_payload)
|
|
740
732
|
response.raise_for_status()
|
|
@@ -744,9 +736,9 @@ async def reply_to_email_async(
|
|
|
744
736
|
"mailbox_email_id": sent_message['id'],
|
|
745
737
|
"message_id": sent_message['threadId'],
|
|
746
738
|
"email_subject": subject,
|
|
747
|
-
"email_sender": sender_email,
|
|
739
|
+
"email_sender": reply_email_context.sender_email,
|
|
748
740
|
"email_recipients": [to_addresses] + ([cc_addresses] if cc_addresses else []),
|
|
749
|
-
"read_email_status": 'READ' if mark_as_read.lower() == "true" else 'UNREAD',
|
|
741
|
+
"read_email_status": 'READ' if reply_email_context.mark_as_read.lower() == "true" else 'UNREAD',
|
|
750
742
|
"email_labels": sent_message.get('labelIds', [])
|
|
751
743
|
}
|
|
752
744
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/check_linkedin_url_validity.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/compose_three_step_workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/create_list_from_sales_navigator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/extract_email_content_for_llm.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_linkedin_connect_message.py
RENAMED
|
File without changes
|
{dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_linkedin_response_message.py
RENAMED
|
File without changes
|
{dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/generate_structured_output_internal.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev28 → dhisana-0.0.1.dev29}/src/dhisana/utils/openai_assistant_and_file_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|