Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email attach fix #1015

Merged
merged 7 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions superagi/tools/email/send_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class SendEmailInput(BaseModel):
to: str = Field(..., description="Email Address of the Receiver, default email address is 'example@example.com'")
subject: str = Field(..., description="Subject of the Email to be sent")
body: str = Field(..., description="Email Body to be sent")
body: str = Field(..., description="Email Body to be sent, Do not add senders details in the email body and end it with Warm Regards without entering any name.")


class SendEmailTool(BaseTool):
Expand Down Expand Up @@ -42,9 +42,9 @@ def _execute(self, to: str, subject: str, body: str) -> str:
"""
email_sender = self.get_tool_config('EMAIL_ADDRESS')
email_password = self.get_tool_config('EMAIL_PASSWORD')
if email_sender == "" or email_sender.isspace():
if email_sender is None or email_sender == "" or email_sender.isspace():
return "Error: Email Not Sent. Enter a valid Email Address."
if email_password == "" or email_password.isspace():
if email_password is None or email_password == "" or email_password.isspace():
return "Error: Email Not Sent. Enter a valid Email Password."
message = EmailMessage()
message["Subject"] = subject
Expand Down
43 changes: 21 additions & 22 deletions superagi/tools/email/send_email_attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
class SendEmailAttachmentInput(BaseModel):
to: str = Field(..., description="Email Address of the Receiver, default email address is 'example@example.com'")
subject: str = Field(..., description="Subject of the Email to be sent")
body: str = Field(..., description="Email Body to be sent")
body: str = Field(..., description="Email Body to be sent, Do not add senders details in the email body and end it with Warm Regards without entering any name.")
filename: str = Field(..., description="Name of the file to be sent as an Attachment with Email")


Expand Down Expand Up @@ -63,30 +63,40 @@ def _execute(self, to: str, subject: str, body: str, filename: str) -> str:
session=self.toolkit_config.session,
agent_execution_id=self.agent_execution_id)
)
if final_path is None or not os.path.exists(final_path):
raise FileNotFoundError(f"File '{filename}' not found.")
attachment = os.path.basename(final_path)
return self.send_email_with_attachment(to, subject, body, final_path, attachment)
ctype, encoding = mimetypes.guess_type(final_path)
if ctype is None or encoding is not None:
ctype = "application/octet-stream"
maintype, subtype = ctype.split("/", 1)
if StorageType.get_storage_type(get_config("STORAGE_TYPE", StorageType.FILE.value)) == StorageType.S3:
attachment_data = S3Helper().read_binary_from_s3(final_path)
else:
if final_path is None or not os.path.exists(final_path):
raise FileNotFoundError(f"File '{filename}' not found.")
with open(final_path, "rb") as file:
attachment_data = file.read()
attachment = MIMEApplication(attachment_data)
attachment.add_header('Content-Disposition', 'attachment', filename=final_path.split('/')[-1])

return self.send_email_with_attachment(to, subject, body, attachment)

def send_email_with_attachment(self, to, subject, body, attachment_path, attachment) -> str:
def send_email_with_attachment(self, to, subject, body, attachment) -> str:
"""
Send an email with attachment.
Args:
to : The email address of the receiver.
subject : The subject of the email.
body : The body of the email.
attachment_path : The path of the file to be sent as an attachment with the email.
attachment : The name of the file to be sent as an attachment with the email.
attachment : The data of the file to be sent as an attachment with the email.
Returns:
"""
email_sender = self.get_tool_config('EMAIL_ADDRESS')
email_password = self.get_tool_config('EMAIL_PASSWORD')
if email_sender == "" or email_sender.isspace():
if email_sender is None or email_sender == "" or email_sender.isspace():
return "Error: Email Not Sent. Enter a valid Email Address."
if email_password == "" or email_password.isspace():
if email_password is None or email_password == "" or email_password.isspace():
return "Error: Email Not Sent. Enter a valid Email Password."
message = MIMEMultipart()
message["Subject"] = subject
Expand All @@ -96,18 +106,7 @@ def send_email_with_attachment(self, to, subject, body, attachment_path, attachm
if signature:
body += f"\n{signature}"
message.attach(MIMEText(body, 'plain'))
if attachment_path:
ctype, encoding = mimetypes.guess_type(attachment_path)
if ctype is None or encoding is not None:
ctype = "application/octet-stream"
maintype, subtype = ctype.split("/", 1)
if StorageType.get_storage_type(get_config("STORAGE_TYPE", StorageType.FILE.value)) == StorageType.S3:
attachment_data = S3Helper().read_binary_from_s3(attachment_path)
else:
with open(attachment_path, "rb") as file:
attachment_data = file.read()
attachment = MIMEApplication(attachment_data)
attachment.add_header('Content-Disposition', 'attachment', filename=attachment_path.split('/')[-1])
if attachment:
message.attach(attachment)

send_to_draft = self.get_tool_config('EMAIL_DRAFT_MODE') or "FALSE"
Expand Down
12 changes: 8 additions & 4 deletions tests/unit_tests/tools/email/test_send_email_attachment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import unittest
from unittest.mock import patch, Mock, MagicMock
from unittest.mock import patch, Mock, MagicMock, ANY
from superagi.models.agent import Agent
import os
from superagi.tools.email.send_email_attachment import SendEmailAttachmentTool, SendEmailAttachmentInput
Expand All @@ -10,9 +10,11 @@ class TestSendEmailAttachmentTool(unittest.TestCase):
@patch("superagi.helper.resource_helper.ResourceHelper.get_agent_read_resource_path")
@patch("superagi.helper.resource_helper.ResourceHelper.get_root_input_dir")
@patch("os.path.exists")
def test__execute(self, mock_exists, mock_get_root_input_dir, mock_get_agent_resource_path,
@patch("superagi.helper.s3_helper.S3Helper.read_binary_from_s3")
def test__execute(self, mock_s3_file_read, mock_exists, mock_get_root_input_dir, mock_get_agent_resource_path,
mock_send_email_with_attachment, mock_get_agent_from_id):
# Arrange

# arrange
tool = SendEmailAttachmentTool()
tool.agent_id = 1
mock_exists.return_value = True
Expand All @@ -23,13 +25,15 @@ def test__execute(self, mock_exists, mock_get_root_input_dir, mock_get_agent_res
mock_get_agent_from_id.return_value = Agent(id=1, name='Test Agent')
tool.agent_execution_id = 1
tool.toolkit_config.session = MagicMock()
mock_s3_file_read.return_value = b"file contents"

# Act
result = tool._execute("test@example.com", "test subject", "test body", "test.txt")

# Assert
self.assertEqual(result, expected_result)
mock_send_email_with_attachment.assert_called_once_with("test@example.com", "test subject", "test body", "/test/path/test.txt", "test.txt")
mock_send_email_with_attachment.assert_called_once_with("test@example.com", "test subject", "test body", ANY)
mock_s3_file_read.assert_called_once_with("/test/path/test.txt")

if __name__ == "__main__":
unittest.main()
Loading