AWS SES Send Email to Multiple Recipient using Python - python

I am new to AWS Lambda function.
I wanted to send email to multiple recipients. I am able to send email to single email address but not multiple email ids and shows error.
I just refered the amazon documentation page and wrote the below code.
I am using environmental variable runteam and it has values like ['aaa#xyz.com','bbb#xyz.com','ccc#xyz.com']
import boto3
import os
import os, sys, subprocess
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
def lambda_handler(event,context):
ses = boto3.client("ses")
s3 = boto3.client("s3")
runemail = os.environ['runteam']
for i in event["Records"]:
action = i["eventName"]
#ip = i["requestParameters"]["soruceIPAddress"]
bucket_name = i["s3"]["bucket"]["name"]
object = i["s3"]["object"]["key"]
fileObj = s3.get_object(Bucket = bucket_name, Key = object)
file_content = fileObj["Body"].read()
sender = "test#xyz.com"
to = runemail
subject = str(action) + 'Event from ' + bucket_name
body = """
<br>
This email is to notify regarding {} event
This object {} is created
""".format(action,object)
msg = MIMEMultipart('alternative')
msg["Subject"] = subject
msg["From"] = sender
msg["To"] = ', '.join(runemail)
body_txt = MIMEText(body, "html")
attachment = MIMEApplication(file_content)
attachment.add_header("Content-Disposition","attachment", filename = "ErrorLog.txt")
msg.attach(body_txt)
msg.attach(attachment)
response = ses.send_raw_email(Source = sender, Destinations = rumemail, RawMessage = {"Data": msg.as_string()})
return "Thanks"

I think everything seems to be right regarding the email sending code. The error lies in your program where the way you store your environ variable.
It should be stored as runteam="aaa#xyz.com bbb#xyz.com ccc#xyz.com" (notice the space between each email)
Then use this variable as
rumemail = os.environ['runteam'].split()
msg["To"] = ', '.join(runemail)
response = ses.send_raw_email(Source = sender, Destinations = rumemail, RawMessage = {"Data": msg.as_string()})

This line:
msg["To"] = ', '.join(runemail)
is expecting a Python list, not a string. I suggest you add a debug line after it to see what you are actually sending the system.
I would recommending passing your environment variable as:
person#address.com, person2#address.com, person3#address.com
Then, use:
msg["To"] = runemail

Removing/commenting out the following line should fix the issue:
msg["To"] = ', '.join(runemail)
By the above line, you are converting a list to a string. However, Destinations attribute is looking for a list.

Related

Looping thru file types and attaching them into email

I have a list of text files and html files generated by two distinct functions. Each file is labeled signal1.txt, signal2, etc. and signal1.html, signal2.html, etc. I need to send an email with each file pair (signal1.txt and signal1.html, signal2.txt and signal.2.html, and so forth).
I've tried several different ways, but I keep getting just one file pair attached (the last file number whatever it is) over and over. I have no problem sending one file type, but it gets messy when I try with two different files. I'd like to give you as much info as possible and perhaps enough reproducible code for you to try it out on your end if you wish, so my apologies for the long question.
The data is collected from the server. The final result is sorted using the Counter module:
data = Counter({('A user account was locked out ', 47, 'medium', 25): 1, ('An attempt was made to reset an accounts password ', 73, 'high', 2): 1, ('PowerShell Keylogging Script', 73, 'high', 37): 1, ('PowerShell Suspicious Script with Audio Capture Capabilities', 47, 'medium', 36): 1})
I need the rule name to be used in the email subject, so everything else is junk. For instance, in ('A user account was locked out ', 47, 'medium', 25): 1, I only need A user account was locked out. So the following function takes care of all that:
def create_txt_files():
global regex
global count
count = 0
#Convert dict into string and remove unwanted chars
for signal in dict(event_dict).keys():
indiv_signal = (str(signal).replace(",",'').replace('(','').replace(')','')\
.replace("'",'').replace('[','').replace(']',''))
#Further removal of debris using regex
pattern = '^(\D*)'
regex = ''.join(re.findall(pattern,indiv_signal,re.MULTILINE))
count +=1
with open(f"signal{count}.txt", "w") as fh:
fh.write(str(regex))
create_txt_files()
I also need to create html files that will go in the body of the email as a Dataframe. In this case I need almost all the fields in the data file. The dataframe should look like this:
Alert Score Risk Severity Total
0 A user account was locked out 47 medium 26
The following function takes care of that:
#Create Individual HTML files
def create_indiv_html_files():
global html_file
global count
count = 0
#Turn rows into columns
for items in list(event_dict):
df = pd.DataFrame(items)
new_df = df.transpose()
new_df.columns = ['Alert','Score Risk','Severity','Total']
html_file = new_df.to_html()
print(new_df)
count +=1
with open(f'signal{count}.html','w') as wf:
wf.write(html_file)
create_indiv_html_files()
So, up to this point everything is fine and dandy, albeit not as pretty a code as I'd like. But it works, and that's all I'm worried about now. The problem is that when I send the email, I'm getting only one rule (the last one) sent over and over. It's not iterating over the txt and html files and attaching them as it should.
Here is the email function I'm using. Despite my several different attempts, I still have not been able to figure out what's wrong. Thank you for taking the time to help.
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders
import smtplib, ssl
import os
dirname = r'C:\Path\To\Files'
ext = ('.txt','html')
for files in os.scandir(dirname):
if files.path.endswith(ext):
def sendmail():
html_body = '''
<html>
<body>
<p style="font-size: 12;"> <strong>Alert</strong><br>{html_file}</p>
</body>
</html>
'''.format(html_file=html_file)
subject = f'Alert: {regex} '
senders_email = 'mail#mail.comt'
receiver_email = 'mail#mail.comt'
# Create a multipart message and set headers
message = MIMEMultipart('alternative')
message['From'] = senders_email
message['To'] = receiver_email
message['Subject'] = subject
#Attach email body
message.attach(MIMEText(html_body, 'html'))
# Name of the file to be attached
filename = f'signal{count}.html'
# Open file in binary mode
with open(filename, 'rb') as attachment:
# Add file as application/octet-stream
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
# Encodes file in ASCII characters to send via email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
'Content-Disposition',
f"attachment; filename= {filename}",
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log into server using secure connection
context = ssl.create_default_context()
with smtplib.SMTP("smtp.mail.com", 25) as server:
# server.starttls(context=context)
# server.login(senders_email, 'password')
server.sendmail(senders_email, receiver_email, text)
print("Email sent!")
sendmail()
I rewrote the code and removed the global variables. Below code should work let me know if you get any errors.
import pathlib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders
import smtplib, ssl
import os
def create_txt_files(event_dict):
regex = []
count = 0
#Convert dict into string and remove unwanted chars
for signal in dict(event_dict).keys():
indiv_signal = (str(signal).replace(",",'').replace('(','').replace(')','')\
.replace("'",'').replace('[','').replace(']',''))
#Further removal of debris using regex
pattern = '^(\D*)'
regex.append(''.join(re.findall(pattern,indiv_signal,re.MULTILINE)))
count +=1
with open(f"signal{count}.txt", "w") as fh:
fh.write(str(regex[0]))
return regex
def create_indiv_html_files(event_dict):
html_file = []
count = 0
#Turn rows into columns
for items in list(event_dict):
df = pd.DataFrame(items)
new_df = df.transpose()
new_df.columns = ['Alert','Score Risk','Severity','Total']
html_file.append(new_df.to_html())
print(new_df)
count +=1
with open(f'signal{count}.html','w') as wf:
wf.write(html_file[0])
return html_file
def sendmail(html_file, regex, path_html):
html_body = '''
<html>
<body>
<p style="font-size: 12;"> <strong>Alert</strong><br>{html_file}</p>
</body>
</html>
'''.format(html_file=html_file)
subject = f'Alert: {regex} '
senders_email = 'mail#mail.comt'
receiver_email = 'mail#mail.comt'
# Create a multipart message and set headers
message = MIMEMultipart('alternative')
message['From'] = senders_email
message['To'] = receiver_email
message['Subject'] = subject
#Attach email body
message.attach(MIMEText(html_body, 'html'))
# Name of the file to be attached
# filename = f'signal{count}.html'
# Open file in binary mode
with open(path_html, 'rb') as attachment:
# Add file as application/octet-stream
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
# Encodes file in ASCII characters to send via email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
'Content-Disposition',
f"attachment; filename= {path_html.name}",
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log into server using secure connection
context = ssl.create_default_context()
with smtplib.SMTP("smtp.mail.com", 25) as server:
# server.starttls(context=context)
# server.login(senders_email, 'password')
server.sendmail(senders_email, receiver_email, text)
print("Email sent!")
data = Counter({('A user account was locked out ', 47, 'medium', 25): 1, ('An attempt was made to reset an accounts password ', 73, 'high', 2): 1, ('PowerShell Keylogging Script', 73, 'high', 37): 1, ('PowerShell Suspicious Script with Audio Capture Capabilities', 47, 'medium', 36): 1})
regex = create_txt_files(data)
html_file = create_indiv_html_files(data)
signalfiles = sorted(list(pathlib.Path('C:\Path\To\Files').glob('*.txt')))
htmlfiles = sorted(list(pathlib.Path('C:\Path\To\Files').glob('*.html')))
for i, path_html_file in enumerate(htmlfiles):
sendmail(html_file[i], regex[i], path_html_file)
The create_txt_files and create_indiv_html_files takes input the Counter dictionary. Sendmail function will take in regex, html_file string and html_file path.

PYTHON: Sending email with an attachment that updates every 15 minutes

Below is a section of my code, it is in a while true loop to collect CO2 and Temperature readings from sensors every 15 minutes. It exports the data to a new CSV file every 15 minutes. The file name changes every 15 minutes as there is a date and time stamp required in the file name.
Can python be used to send an email with the CSV file attached if the file name is constantly changing?
I found the example below the dashed line online but the file location would be changing every 15 minutes in my case
for device in list_of_devices:
print (device)
for url_CO2 in list_of_urls_CO2:
headers = CaseInsensitiveDict()
headers['Accept'] = 'application/json'
headers['Authorization'] = bearer_token
resp_CO2 = requests.get(url_CO2, headers=headers)
response_CO2.append(resp_CO2.text)
print(resp_CO2.text)
for url_RT in list_of_urls_RT:
headers = CaseInsensitiveDict()
headers['Accept'] = 'application/json'
headers['Authorization'] = bearer_token
resp_RT = requests.get(url_RT, headers=headers)
response_RT.append(resp_RT.text)
print(resp_RT.text)
# importing pandas as pd
import pandas as pd
# dictionary of lists
myDict = {'Device': list_of_devices, 'CO2 Level': response_CO2, 'Room Temperature': response_RT}
df = pd.DataFrame(myDict)
# saving the dataframe
df.to_csv('test_{}.csv'.format(datetime.now().strftime("%Y-%m-%d %H.%M.%S")), index=False)
----------------------------------------------------------------------------------------
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import os.path
email = 'myaddress#gmail.com'
password = 'password'
send_to_email = 'sentoaddreess#gmail.com'
subject = 'Town Farm Data Log'
message = 'Please see attached CO2 levels and Room Temperatures for Town Farm Classrooms'
file_location = 'C:\\Users\\You\\Desktop\\attach.txt'
msg = MIMEMultipart()
msg['From'] = email
msg['To'] = send_to_email
msg['Subject'] = subject
msg.attach(MIMEText(message, 'plain'))
filename = os.path.basename(file_location)
attachment = open(file_location, "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(email, password)
text = msg.as_string()
server.sendmail(email, send_to_email, text)
server.quit()
If you don't need to keep your files after send, it's easy to do like this:
Put your sending email code into the function named send_by_email for example
Then just collect all the files in the target dir, filter only csv-s and send them one by one (don't forget to delete file after send not to get into eternal loop on next iteration)
import os
mypath = '/tmp' # define path to dir with your files
filenames = next(os.walk(mypath), (None, None, []))[2] # list all files
csvs = [f for f in filenames if f.endswith('.csv')] # fileter only .scv files
for filename in csvs:
file_with_path = os.path.join(mypath, filename)
send_by_email(file_with_path)
os.remove(file_with_path)
If you need to keep files, you can just move them into another dir instead of deleting.

Python Command '[ ... ]' returned non-zero exit status 1

I'm trying to get a file from BigQuery, convert it to excel format and sending it attached in an email. Here is my code:
def run(**kwargs):
from datetime import datetime
from google.cloud import bigquery
import pandas as pd
def from_bq_to_dataframe(sql_location):
client = bigquery.Client()
with open(sql_location) as file:
sql = file.read()
df = client.query(sql).to_dataframe()
return df
def filter_and_excel(df):
df = df.nlargest(1000, ['cant_form_final'])
writer = pd.ExcelWriter('Document_name' + '_' + datetime.now().strftime('%Y/%m/%d'))
df = df.to_excel(writer, 'review_document') #Sheet name
return df
def send_mail(subject, text, send_to, send_to_cc=[], file=None):
import smtplib
from email.mime.multipart import MIMEMultipart
username = "user#gmail.com"
password = "*****"
msg = MIMEMultipart()
msg['From'] = username
msg['To'] = ', '.join(send_to)
msg['Cc'] = ', '.join(send_to_cc)
msg['Subject'] = subject
msg.attach(file)
server = smtplib.SMTP(host="host_address", port=587)
server.starttls()
server.login(username, password)
server.sendmail(username, send_to, msg.as_string())
server.quit()
subject = 'review ' + datetime.now().strftime('%Y-%m-%d')
text = 'Hi,\n\n' \
'I attach the review.\n\n'
send_mail(subject=subject,
text=text,
send_to=['mail#gmail.com'],
send_to_cc=['mail1#gmail.com'],
file=[filter_and_excel(from_bq_to_dataframe(**kwargs))]
)
When I try it locally I have no problems, but when I try linking it to big query I get this error:
Exception:
Command '['/tmp/venvq6e8hcce/bin/python', '/tmp/venvq6e8hcce/script.py', '/tmp/venvq6e8hcce/script.in', '/tmp/venvq6e8hcce/script.out', '/tmp/venvq6e8hcce/string_args.txt']' returned non-zero exit status 1.
Does anyone know why is that error happening? What does it mean?
Every similar error I found comes from installing python itself or maybe pip. Maybe I have some problem with the python version? I'm using 3.8
Hope someone can help me!

CC email and multiple TO email addresses not working

I've created a function in Python which will build an email in html format and send it anonymously through an Exchange server which accepts anonymous email sends.
However, no one in the CC list gets a copy and only the FIRST person in the TO list gets a copy. I've tried 'comma' and 'semicolon' as the email separator but neither works. Curiously, in the email received ALL of the email addresses can be seen (and in the correct format) despite the fact that only one person (the person listed first in the TO list) gets a copy.
This is my code
def send_email(sender, subject, sendAsDraft = True): # sendAsDraft not implemented
global toEmail
global ccEmail
# create the email message
htmlFile = open(saveFolder + '\\' + htmlReportBaseName + '.html',encoding = 'utf-8')
message = htmlFile.read().replace('\n', '')
message = message.replace(chr(8217), "'") # converts a strange single quote into the standard one
message = message.replace("'",''') # makes a single quote html friendly
htmlFile.close()
# get the email body text
emailBody = open('emailBody.txt')
bodyText = emailBody.read()
emailBody.close()
now = dt.datetime.today().replace(second = 0, microsecond = 0)
timeStr = returnReadableDate(now) # returns the date as a string in the 'normal' reading format
# insert the current date/time into the marker in the email body
bodyText = bodyText.replace('xxxx', timeStr)
# insert the email body text into the HTML
message = message.replace('xxxx', bodyText)
msg = MIMEMultipart('Report Email')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = '; '.join(toEmail)
msg['Cc'] = '; '.join(ccEmail)
msg.add_header('Content-Type','text/html')
msg.set_payload(message)
# complete the email send
message = ''
try:
smtpObj = sl.SMTP('1.2.3.4:5') # obviously IP address anonymised
smtpObj.sendmail(sender, '; '.join(toEmail), msg.as_string())
message = 'Email successfully sent'
except:
message = 'Email could not be sent due to an error'
finally:
# write the outcome to the Instance log
global InstanceLog
# write the log
log(InstanceLog, message)
# close object and exit function
smtpObj.quit()
Is it possible that the issue is at the email server? That where an un-authenticated email can only be sent to one person? That would seem strange in one way, and make sense in another.
Thanks all.
Seamie
Beware. send_message can use the headers of a message to build its recipient list, but sendmail requires a list of addresses or handle a string as a single recipicient address.
And the headers should contain comma instead of semicolons.
So you should have:
...
msg['To'] = ' '.join(toEmail)
msg['Cc'] = ','.join(ccEmail)
...
and later either:
smtpObj.sendmail(sender, toEmail + ccEmail, msg.as_string())
or:
smtpObj.send_message(msg)
Looking at the docs, this should be a comma seperated string:
msg['To'] = ', '.join(toEmail)

Send mail in Lotus Notes using python

i need help for send a mail in the Lotus Notes using python, appear that the win32com can do it, but i don't found any complete example or tutorial. My idea is a simple function like it:
from win32com.client import Dispatch
import smtplib
def SendMail(subject, text, user):
session = Dispatch('Lotus.NotesSession')
session.Initialize('???')
db = session.getDatabase("", "")
db.OpenMail();
Some suggestion? Thanks!
Below is some code that I have used for this purpose for several years:
from __future__ import division, print_function
import os, uuid
import itertools as it
from win32com.client import DispatchEx
import pywintypes # for exception
def send_mail(subject,body_text,sendto,copyto=None,blindcopyto=None,
attach=None):
session = DispatchEx('Lotus.NotesSession')
session.Initialize('your_password')
server_name = 'your/server'
db_name = 'your/database.nsf'
db = session.getDatabase(server_name, db_name)
if not db.IsOpen:
try:
db.Open()
except pywintypes.com_error:
print( 'could not open database: {}'.format(db_name) )
doc = db.CreateDocument()
doc.ReplaceItemValue("Form","Memo")
doc.ReplaceItemValue("Subject",subject)
# assign random uid because sometimes Lotus Notes tries to reuse the same one
uid = str(uuid.uuid4().hex)
doc.ReplaceItemValue('UNIVERSALID',uid)
# "SendTo" MUST be populated otherwise you get this error:
# 'No recipient list for Send operation'
doc.ReplaceItemValue("SendTo", sendto)
if copyto is not None:
doc.ReplaceItemValue("CopyTo", copyto)
if blindcopyto is not None:
doc.ReplaceItemValue("BlindCopyTo", blindcopyto)
# body
body = doc.CreateRichTextItem("Body")
body.AppendText(body_text)
# attachment
if attach is not None:
attachment = doc.CreateRichTextItem("Attachment")
for att in attach:
attachment.EmbedObject(1454, "", att, "Attachment")
# save in `Sent` view; default is False
doc.SaveMessageOnSend = True
doc.Send(False)
if __name__ == '__main__':
subject = "test subject"
body = "test body"
sendto = ['abc#def.com',]
files = ['/path/to/a/file.txt','/path/to/another/file.txt']
attachment = it.takewhile(lambda x: os.path.exists(x), files)
send_mail(subject, body, sendto, attach=attachment)
If your company is set up such a way that a host IP address and Port is sufficient to send an email, then use the following:
import smtplib
from email.mime.base import MIMEBase
from email import encoders
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def emailReport(attach_path,file_name,From,to,cc=None):
msg = MIMEMultipart()
msg['Subject'] = file_name
msg['From'] = From
msg['To'] = ", ".join(to)
msg['CC'] = ", ".join(cc)
msg.attach(MIMEText("****Body of your email****\n"))
#For multiple attachments repeat/loop the following:
part = MIMEBase('application', "octet-stream")
part.set_payload(open(attach_path.format(file_name), "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename=%s' % file_name)
msg.attach(part)
#Repeat stops here....
s = smtplib.SMTP(host='xxx.xxx.xx.x',port=xx) #Enter your IP and port here
s.sendmail(From,to+cc,msg.as_string())
s.quit()
to=['Person1#email.com', 'Person2#email.com']
cc=['Person3#email.com', 'Person4#email.com']
From='Your#email.com'
attach_path=r'C:\Users\Desktop\Temp'
file_name='Test.xlsx'
emailReport(attach_path,file_name,From,to,cc)

Categories

Resources