Send Email If Not Sent In Last 24 Hours - python

I'm working on a script in Python that checks an IP address against a blacklist and sends an email only if the IP shows up on the list. The script will be setup to be run every 15 minutes, but I only want it to send an email if the IP is on the list and an email hasn't been sent in the last 24 hours. Current code:
import sys
import subprocess
import smtplib
import datetime
username = ''
password = ''
fromaddr = ''
toaddr = ''
server = smtplib.SMTP(host=,port=)
server.starttls()
server.ehlo()
server.esmtp_features["auth"] = "LOGIN PLAIN"
server.login(username,password)
sentFolder = server.select("SENT",readonly=TRUE)
recentSent = sentFolder["Date"]
OneDayAgo = date.today()-timedelta(days=1)
msg = ''
staticIPAddress = ''
dnsHostname = staticIPAddress + ".bl.spamcop.net"
p = subprocess.check_output("nslookup " + dnsHostname1,stderr=subprocess.STDOUT,shell=False)
if ('Non-existent' not in str(p) and recentSent < OneDayAgo):
server.sendmail(fromaddr, toaddrs, msg)
The error I run into occurs at:
sentFolder = server.select("SENT",readonly=TRUE)
The error code is:
AttributeError: 'SMTP' object has no attribute 'select'
I've tested the rest of the script (without that piece and without the recentSent < OneDayAgo pieces) and it seems to work fine.
Any help in figuring out how to make the "only send if not sent within the last 24 hours" piece work would be really appreciated.

In order to know if you've sent email in the previous 24 hours, you'll need to make a record of sending the email. You might store that information in a text file, an IMAP folder, a database, through a web app, or many other ways. How you store that data is your design decision.
Here is one possibility, in which the timestamp is stored in the modification date of a local file.
#UNTESTED EXAMPLE CODE
def create_timestamp():
with open("tsfile", "w") as fp:
fp.write("now")
def time_since_last_timestamp():
return time.time() - os.path.getmtime("tsfile")
...
if 'Non-existent' not in str(p) and time_since_last_timestamp() > 86400:
server.sendmail(...)
create_timestamp()

To determine whether or not an email has been sent in the last 24 hours, you might want to program your script to examine the mail server logs. You didn't mention which MTA you are using, but all that I know of log messages in and out.

Related

Allowing users to manage frequency of emails? - Python Bulk Mailer

I have a task to create a bulk mailer in python which sends bulk email content to a list of subscribers - how would I go about inputting code to allow the subscribers to manage the frequency and content of emails they receive?
import pandas as pd
import smtplib
# reading excel email list + retrieving the values
e = pd.read_excel(r"C:\Users\****\OneDrive\Desktop\emailList.xlsx")
email = e['Email'].values
# setting up server to send mail
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login("bulkmailer****#gmail.com", "*****")
msg = "Hi there! Check out these exclusive offers tailored just for you!"
subject = "Exclusive Offers Inside"
body = "Subject : {}\n\n{}".format(subject, msg)
# for loop for server to send emails from server to email list
for email in email:
server.sendmail("bulkmailer****#gmail.com", email, body)
server.quit()
The code you have provided has the effect of sending a single message to every one of your subscribers. To have any "frequency" to speak of, you need to run this program occasionally -- for example, you can set up a cron job (or a Windows equivalent) that executes it once every X time -- say, once per minute.
Won't that mean your subscribers will get spammed with messages once per minute? It would, unless we add something else: a way to record when the message has been sent, or, equivalently, when the next message is due.
Specifically, along with each address, you need to store: the content of the message you'd like to send to them this time; the interval with which you intend to send the messages; and the last time that we sent a message to that address.
For this, normal applications use a database. You are using Pandas Dataframes, which probably have sufficient capabilities, but they're definitely harder to use for this. Since you have said in the comments that this is a homework question, and also because I have no experience with Pandas, I will instead provide some ORM-like pseudocode.
from dataclasses import dataclass
import database
import time
import mailer
#dataclass
class DatabaseRow:
""" Single row in database of email addresses and associated data """
email: str # email address to send message to
subject: str # message subject
body: str # message body
send_interval: int # or float -- number of seconds between each message
next_send_at: Optional[int] # int or None (or float or None); Unix time at which to send next message; if None, send immediately
for row in database.get_all_rows():
current_time = time.time() # this returns Unix time: the number of seconds since 1 Jan 1970
if row.next_send_at is None or current_time < row.next_send_at:
# it is not time to send it to them yet; don't do anything
continue
mailer.send(row.address, row.subject, row.body)
row.next_send_at = current_time + row.send_interval # the next time we'll send a message is (send_interval) away from now
row.save()

How can I get the date recieved / sent from email in python

I have a program that needs to read in emails and validate if they are from this month, before continuing.
I obtain the email info via the following code
import email
import smtplib
import imaplib
mail = imaplib.IMAP4_SSL('redacted', 993)
mail.login(username, bytes(password).decode('utf-8')) #password is bytes that have been decrypted
msg_data2 = [] #My template allows for multiple email data to be appended
mailbox_data = mail.list()
mail.select('INBOX', readonly=True)
result, msg_ids = mail.search(None, f'(SEARCH CRITERIA REDACTED)')
lister = msg_ids[0].split()
most_recent = lister[-1]
result2, msg_data = mail.fetch(most_recent, '(RFC822)')
msg_data2.append(msg_data)
raw = email.message_from_bytes(msg_data[0][1])
from here im able to get attachments from my emails matching the search criteria, and previously, vendors would name the files properly with the month their jobs ran. Now some are not, so Im attempting to just check the date the email was sent or received.
You can get the sending date from the email's 'date' header.
from email import utils
...
raw = email.message_from_bytes(msg_data[0][1])
datestring = raw['date']
print(datestring)
# Convert to datetime object
datetime_obj = utils.parsedate_to_datetime(datestring)
print(repr(datetime_obj))
The Date: header is inserted by the sender, and may or may not be accurate. For example, when I write an email and place it in the outbox, it gets the date and time of me placing it in the outbox in the Date: header. The header remains the same even if I only send the email hours (or possibly days) later.
This still doesn't say anything on when it was received. It may be stuck in transit for days. For that it depends on your mail client. For example, Claws inserts a X-Received header when it fetches mail, and that will have the timestamp when Claws downloaded the email from the server to your local machine. This may be minutes or even days after it arrived in your inbox.
To check when the email actually was received by your email provider, look at the Received: headers. The top header is from your (provider's) mail server. It should end in a time stamp, with a semicolon separating the time stamp from the rest of the header.
All RFC 5322 time stamps can be parsed with email.utils.parsedate.
So the code would be something along those lines:
from email import utils
mail = "..."
sent = mail['date']
print(f"Date header: {sent}")
received = mail['Received'][0]
received = received.split(";")[-1]
print(f"Received: {received}")
sent_ts = utils.parsedate(sent_date)
received_ts = utils.parsedate(received_ts)
time_in_transit = received_ts = sent_ts
print(f"Sent {sent_ts}, received {received_ts}, took {time_in_transit}")

Adding Content-Disposition header in Python - email isn't sent

Following the directions in Python's email examples and in several Stack Overflow questions, I wrote the following function (sending through the Gmail SMTP server):
def send_email(emailaddr, message, attachmentfile = None, subject = None):
try:
smtpconn = smtplib.SMTP(mainconf["SMTPHOST"], mainconf["SMTPPORT"])
smtpconn.set_debuglevel(1)
smtpconn.ehlo()
smtpconn.starttls()
smtpconn.login(mainconf["SMTPUSER"], mainconf["SMTPPASS"])
if not attachmentfile:
message = '\n' + message
smtpconn.sendmail(mainconf["EMAILFROM"], emailaddr, message)
else:
multipart = MIMEMultipart()
multipart['Subject'] = subject if subject else "Attachment"
multipart['From'] = mainconf["EMAILFROM"]
multipart['To'] = emailaddr
with open(attachmentfile, 'rb') as fp:
filepart = MIMEApplication(fp.read())
multipart.attach(filepart)
multipart.attach(message)
smtpconn.sendmail(mainconf["EMAILFROM"], emailaddr, multipart.as_string())
generallog.info("Sent an email to {0}".format(emailaddr))
except:
generallog.warn("Email sending to {0} failed with error message {1}".format(emailaddr, traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1])))
This works fine for sending an email with an attachment, but it results in the famous noname problem (that's just one of several SO questions on the subject). So I add in this:
filepart.add_header('Content-Disposition','attachment',filename=os.basename(attachmentfile))
Then Gmail simply refuses to send the email. When I run this code in the Python shell, I get the standard message accepted message, but the message is never delivered.
Why might this be happening?

how to sign request tokens?

I am currently trying to write a script to send off a request token, I have the header, and the claimset, but I don't understand the signature! OAuth requires my private key to be encrypted with SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function), but the closest I could find was RSAES-PKCS1-v1_5 (has RSA, and the SHA-256 hash). I followed the example, and tweaked it, so I could get it set, but heres my dillema:
signature = ""
h = SHA.new (signature)
key = RSA.importKey(open('C:\Users\Documents\Library\KEY\My Project 905320c6324f.json').read())
cipher = PKCS1_v1_5.new(key)
ciphertext = cipher.encrypt(message+h.digest())
print(ciphertext)
I'm a bit lost, the JSON file I was given has both public key, and private, do I copy and paste the private key into the signature variable (it gave me a invalid syntax)? Or do I past the directory again? I am so lost, and way over my head haha. I am currently running Python 3.4, with pyCrypto for the signature.
Based on what you've said below about wanting to write a command system using gmail, I wrote a simple script to do this using IMAP. I think this is probably simpler than trying to use Google APIs for a single user, unless you were wanting to do that simply for the exercise.
import imaplib, logging
from time import sleep
USERNAME = 'YOUR_USERNAME_HERE' # For gmail, this is your full email address.
PASSWORD = 'YOUR_PASSWORD_HERE'
CHECK_DELAY = 60 # In seconds
LOGGING_FORMAT = '%(asctime)s %(message)s'
logging.basicConfig(filename='imapTest.log', format=LOGGING_FORMAT, level=logging.INFO)
logging.info("Connecting to IMAP server...")
imap = imaplib.IMAP4_SSL('imap.gmail.com')
imap.login(USERNAME, PASSWORD)
logging.info("Connected to IMAP server.")
def get_command_messages():
logging.info("Checking for new commands.")
imap.check()
# Search the inbox (server-side) for messages containing the subject 'COMMAND' and which are from you.
# Substitute USERNAME below for the sending email address if it differs.
typ, data = imap.search(None, '(FROM "%s" SUBJECT "COMMAND")' %(USERNAME))
return data[0]
def delete_messages(message_nums):
logging.info("Deleting old commands.")
for message in message_nums.split():
imap.store(message, '+FLAGS', '\\DELETED')
imap.expunge()
# Select the inbox
imap.select()
# Delete any messages left over that match commands, so we are starting 'clean'.
# This probably isn't the nicest way to do this, but saves checking the DATE header.
message_nums = get_command_messages()
delete_messages(message_nums)
try:
while True:
sleep(CHECK_DELAY)
# Get the message body and sent time. Use BODY.PEEK instead of BODY if you don't want to mark the message as read, but we're deleting it anyway below.
message_nums = get_command_messages()
if message_nums:
# search returns space-separated message IDs, but we need them comma-separated for fetch.
typ, messages = imap.fetch(message_nums.replace(' ', ','), '(BODY[TEXT])')
logging.info("Found %d commands" %(len(messages[0])))
for message in messages[0]:
# You now have the message body in the message variable.
# From here, you can check against it to perform commands, e.g:
if 'shutdown' in message:
print("I got a shutdown command!")
# Do stuff
delete_messages(message_nums)
finally:
try:
imap.close()
except:
pass
imap.logout()
If you're set on using the Gmail API, though, Google strongly encourage you to use their existing Python library rather than attempt to do full authentication etc. yourself as you appear to be. With that, it should - more or less - be a case of replacing the imap calls above with the relevant Gmail API ones.

Wrong "uid" when searching with imaplib

I am experiencing a strange problem while developing my python application.
The application is supposed to parse a mail inbox for the unread messages, get a specific ones, process the body and store it into a database.
Everything seem to be working fine for my first 7 mails from my domain, but with the last one and the 4 #gmails, the results don't match with the expected, the mails stored into the database aren't the correct ones, in fact, they are exactly the 4th mail after the correct one.
I am showing the code that I developed, don't be too hard with me, I am kinda new coding:
main.py
from src_pckg import reader
reader.read("***#m***p.com", "***", "imap.***.com", 993, "noreply#s***d.com")
reader.py
def read(username, password, host, port, sender_of_interest):
#Con details
imap_con = imaplib.IMAP4_SSL(host, port)
imap_con.login(username, password)
imap_con.select("INBOX")
#Print all unread messages from a certain sender
status, response = imap_con.search(None, 'UNSEEN', '(FROM "%s")' % (sender_of_interest))
unread_msg_nums = response[0].split()
print(len(unread_msg_nums))
for e_id in unread_msg_nums:
status, data = imap_con.uid('fetch', e_id, '(RFC822)')
msg = data[0][1].decode(encoding='UTF-8')
if re.search("has given you a gift subscription", msg):
#Process the mail
return True

Categories