Looping thru file types and attaching them into email - python

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.

Related

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.

Use python to download email attachments only based on Subject

The following code uses imap to find emails by subject line and returns all parts of the email and downloads the attachments. However i am ONLY needing it to download the attachments of the email not the entire body also. I understand this has to do with the for part in email_message.walk(): that is iterating the entire email. Could someone please help me have this code download only the attachment of the email? Im sure this is a simple code change but im just not sure how to make it!
import imaplib
import email.header
import os
import sys
import csv
# Your IMAP Settings
host = 'imap.gmail.com'
user = 'User email'
password = 'User password'
# Connect to the server
print('Connecting to ' + host)
mailBox = imaplib.IMAP4_SSL(host)
# Login to our account
mailBox.login(user, password)
boxList = mailBox.list()
# print(boxList)
mailBox.select()
searchQuery = '(SUBJECT "CDR Schedule output from schedule: This is a test to see how it works")'
result, data = mailBox.uid('search', None, searchQuery)
ids = data[0]
# list of uids
id_list = ids.split()
i = len(id_list)
for x in range(i):
latest_email_uid = id_list[x]
# fetch the email body (RFC822) for the given ID
result, email_data = mailBox.uid('fetch', latest_email_uid, '(RFC822)')
# I think I am fetching a bit too much here...
raw_email = email_data[0][1]
# converts byte literal to string removing b''
raw_email_string = raw_email.decode('utf-8')
email_message = email.message_from_string(raw_email_string)
# downloading attachments
for part in email_message.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
fileName = part.get_filename()
if bool(fileName):
filePath = os.path.join('C:/install files/', fileName)
if not os.path.isfile(filePath) :
fp = open(filePath, 'wb')
fp.write(part.get_payload(decode=True))
fp.close()
subject = str(email_message).split("Subject: ", 1)[1].split("\nTo:", 1)[0]
print('Downloaded "{file}" from email titled "{subject}" with UID {uid}.'.format(file=fileName, subject=subject, uid=latest_email_uid.decode('utf-8')))
mailBox.close()
mailBox.logout()

AWS SES Send Email to Multiple Recipient using 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.

How to change actual location to temporary, add headers and remove space in csv file on Python Email table from MySQL?

I have a python code that run a MySQL query and send a email with csv attachment. I need to make following three changes. Can anybody please help me with this?
I need to change the actual location to a temporary location (I tired, it gaves me an error then I chnage it to the real location)
I have no headers on my result csv file. How do I add header on result csv?
It puts a empty raw between each raw, how do I remove that too.
Code -
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.base import MIMEBase
import mysql.connector
import csv
my_db = mysql.connector.connect(
host="localhost",
user="root",
passwd="admin",
database="simplymacstaging"
)
my_cursor = my_db.cursor()
my_cursor.execute("SELECT CONVERT(DateCreated, Date) 'Date', StoreName, ROUND(sum(TotalCost), 2) 'TotalCost' "
"FROM simplymacstaging.ajdustmenthistory WHERE ReasonCode = 'Negligence - Service' AND "
"CONVERT(DateCreated, Date) >= '2019-02-03' GROUP by StoreName, Date")
databases = my_cursor.fetchall()
fp = open('C:\#Emailproject/emailtest.csv', 'w')
attach_file = csv.writer(fp)
attach_file.writerows(databases)
fp.close()
# My email
fromaddr = "******************#gmail.com"
# EMAIL ADDRESS YOU SEND TO
toaddr = "**********#gmail.com"
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = "Test Email Data" # SUBJECT OF THE EMAIL
body = "Hello, Please see the attched report for week. Thank you, ****"
msg.attach(MIMEText(body, 'plain'))
filename = "Test Email.csv" # NAME OF THE FILE WITH ITS EXTENSION
attachment = open("C:\#Emailproject/emailtest.csv", "rb") # PATH OF THE FILE
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(fromaddr, "***********") # YOUR PASSWORD
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)
server.quit()
sample result image -
Thank you so much
to avoid the empty rows try this
fp = open('C:\#Emailproject/emailtest.csv', 'w', newline='')
to add the header row
attach_file.writerow(["date", "address", "col3"])
attach_file.writerows(databases)

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