Flask Mail, Duplicate mails being sent - python

I am using Python and Flask Mail. I am trying to send a welcome email after the user confirms his account via clicking on a link sent as an email to the user earlier for confirming his account. After confirming his account by changing the value of the confirmed entry to true in the database, I send the welcome email. But it is sending 5 duplicate emails instead of one.
I used the below code-
class AccountConfirmation(Resource):
#jwt_required
def post(self):
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
user.confirmed = True
db.session.commit()
send_welcome_email(user)
return {'msg': 'Account confirmed successfully. You can now login to your account'}, 200
def send_welcome_email(user):
subject = '...'
body = '...'
sender = Config.ADMINS[0]
recipients = [user.email_id]
send_email(subject, body, sender, recipients)
from flask_mail import Message
def send_email(subject, body, sender, recipients):
msg = Message(subject = subject, body = body, sender=sender, recipients=recipients)
mail.send(msg)

I don't find any problem with the code. Except if there are multiple request to send the mail. To get over it, you can try sending Asynchronous Email. Please check this piece of code and don't forget to check the link for better explanation.
from threading import Thread
# ...
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(subject, sender, recipients, text_body, html_body):
msg = Message(subject, sender=sender, recipients=recipients)
msg.body = text_body
msg.html = html_body
Thread(target=send_async_email, args=(app, msg)).start()

Related

Getting TypeError: can only concatenate list (not "str") to list, while sending mail from Flask Mail

Below is the code , which throws error in mail.send . The entry goes to database,but email is not getting sent. Tried a lot to resolve, but could not find type mismatch .
#app.route("/participate_save",methods=["POST"])
def participate_save():
if (request.method == "POST"):
'''Add Entry to the Database'''
-------My CODE-------
msg = Message('Hello', sender = [params['mail_user']], recipients =guardian_email)
msg.body = "Hello Flask message sent from Flask-Mail"
mail.send(msg)
# mail.send_message(subject='New message from Admin of website',
# sender=[params['mail_user']],
# recipients=guardian_email,
# body= "Thank You" ,
# )
return redirect(url_for('home'))

How to send an existing message in a new email without losing formatting

Context:
I'm attempting to send an existing email in my inbox to a new thread.
Problem:
I've successfully sent the email body using this function however the body loses the formatting of the original email and only sends text.
I think it makes more sense to add the entire payload to the request body, as documented on the gmail API page "Try this API" section:
However when I add payload to the request body:
def create_message(sender, to, subject, thread_id, message_id, payload, service):
"""Create a message for an email.
Args:
sender: Email address of the sender.
to: Email address of the receiver.
subject: The subject of the email message.
message_text: The text of the email message.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEMultipart('alternative')
message['to'] = to
message['from'] = sender
message['subject'] = 'Re: %s' %subject
return {'raw': raw, 'threadId': thread_id, 'payload': payload}
The emails are sent with no content. How can I add an existing email to a new thread without having to decode and encode and lose the email's formatting?
After messing around I've made two functions that can pass the plain and html content types to a new email for anyone else who might be struggling:
def get_all_parts(service, user_id, msg_id):
message = service.users().messages().get(userId=user_id, id=msg_id, format='raw').execute()
msg_bytes = base64.urlsafe_b64decode(message['raw'].encode('ASCII'))
b = email.message_from_bytes(msg_bytes)
body = []
if b.is_multipart():
for part in b.walk():
if part.get_content_maintype() != 'multipart':
this_part = []
this_part.append(part.get_payload(decode=True))
this_part.append(part.get_content_type())
body.append(this_part)
return body
def create_message(sender, to, subject, thread_id, message_id, message_text, service):
message = MIMEMultipart()
message['to'] = to
message['from'] = sender
message['subject'] = 'Re: %s' %subject
for part in message_text:
text = part[1].split('/') # 'text/plain' -> ['text', 'plain']
new_part = MIMEText(str(part[0]), text[1])
print(part[1])
message.attach(new_part)
raw = base64.urlsafe_b64encode(message.as_string().encode('UTF-8')).decode('ascii')
body = {'raw': raw, 'threadId': thread_id}
enter code here
This is definitely not an exhaustive function for all emails but works for alternative content types.
If you open an email from Gmail UI, click on the three dots and chose "Show original",
you can observe the email structure
Sample:
Message
Show original
You can see that the message is multipart/alternative and contains a text/plain and a text/html part
If you use the whole highlighted code in the sample as the body of your message, the formatting will be preserved. You only need to specify the From, To and Subject headers.

Sending an email in celery while running the rest of the method

Im trying to make my request to a page faster by having the send email part done through celery so that way the rest of the method can run and finish without having to wait for the email to be sent.
Ive tried using async and making a celery call before but I am unskilled and need a little help in that department
def checkout():
if request.method == 'POST':
username = User.get_username(session['email'])
#todo: do the next line in celery
send_email(username, session['email_to'])
session['email_to'] = None
flash('Youre request has been sent')
return redirect(url_for('home'))
return render_template('transaction/checkout.html')
send_email.py
def send_email(username_from, email):
from_email = os.get('email')
from_password = 'rnmmhgtlcaqsxows'
to_email = os.get('email')
subject = f'New Request from {username_from}'
message = f"You have a request from {username_from}"
msg = MIMEText(message, 'html')
msg['subject'] = subject
msg['To'] = to_email
msg['From'] = from_email
gmail = smtplib.SMTP('smtp.gmail.com',587)
gmail.ehlo()
gmail.starttls()
gmail.login(from_email, from_password)
gmail.send_message(msg)
Imagine send_email is a Celery task. - It would be sufficient to have
send_email.apply_async()
in your code to put that task on the default Celery queue, and it will be picked and executed by Celery, while the rest of the code continues working.

Routing Twilio SMS Responses in Python for conversational bot

I'm a university student relatively new to Python programming. I have been trying to build a conversational bot that allows a user to schedule appointments via SMS in Python.
I have been following many Python Twilio SMS tutorials online such as the official Twilio guide. I cannot figure out how to route a Twilio SMS response to the corresponding route. For example, a user is supposed to text in either SCHEDULE, RESCHEDULE, CHECK, or DROP, and the application is supposed to route them to the corresponding route with some logic to process the request (and reply with a further SMS message). If they do not enter one of the four specified words, the application is supposed to reroute the user back to the /inbound route to ask them to again clarify that they are selecting one of the four acceptable options. I have really been struggling hard for the past couple of days on this, getting confused on routes and whether or not to use the message = client.messages.create or response = MessagingResponse() Twilio APIs. I am using Flask to handle the web server capabilities.
Also, would you happen to know how to pass a parameter through the redirect route so that I can use that data in the next route and save it in a database?
Thank you so much. I greatly appreciate all the help I can get.
from twilio.twiml.messaging_response import Body, Message, Redirect, MessagingResponse
from flask import Flask, request, session
app = Flask(__name__)
# Main function triggered by a POST request by ngrok. When an SMS is received, Twilio will send the POST.
#app.route('/inbound', methods=['POST'])
def inbound():
""" Use Twilio API to reply to texts """
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip() # formulate answer to message
if "schedule" == formatted:
reply = 'Hi, my name is Cletus. I\'d like to help you schedule your appointment today. What is your name? ' \
'Please format \"First Last\" (ex: John Smith).'
response.message(reply)
response.redirect('/schedule')
# write to db
elif "reschedule" == formatted:
reply = 'Hi, my name is Cletus. I\'d like to help you reschedule your appointment today. What is your name? ' \
'Please format \"First Last\" (ex: John Smith).'
response.message(reply)
response.redirect('/reschedule')
elif "appointments" == formatted:
reply = 'Hi, my name is Cletus. I\'d like to help you check your upcoming appointment. What is your name? ' \
'Please format \"First Last\" (ex: John Smith).'
response.message(reply)
response.redirect('/check')
elif "drop" == formatted:
reply = 'Hi, my name is Cletus. I\'d like to help you cancel your appointment. What is your name? ' \
'Please format \"First Last\" (ex: John Smith).'
response.message(reply)
response.redirect('/drop')
else:
reply = 'Hi, my name is Cletus. How can I help you? You can text RESCHEDULE to reschedule you appointment, ' \
'DROP to cancel your appointment, and APPOINTMENTS to receive a list of your currently scheduled ' \
'appointments. Have a great day!'
response.message(reply)
response.redirect('/inbound')
return str(response)
#app.route('/schedule', methods=['POST'])
def schedule():
print('Finally routing to schedule')
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip()
#app.route('/reschedule', methods=['POST'])
def reschedule():
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip()
#app.route('/check', methods=['POST'])
def check():
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip()
#app.route('/drop', methods=['POST'])
def cancel():
sender = request.form['From']
message = request.form['Body']
recipient = request.form['To'] # Recipient Phone Number
message_id = request.form['MessageSid'] # Twilio message id
response = MessagingResponse() # init a Twilio response
formatted = message.lower().strip()
if __name__ == '__main__':
app.run(debug = True)

django_cron not sending email

I have just installed django_cron not django_crontab though, somehow I am trying to send an email as notification but it just wouldn't work.
this is just for testing purpose so I set it to 1 minute.
the code was a bit more complex before but it didn't work so I used the most simplified way to send email in order to make sure that it's not working. Also even used it as a post method to test, and it tested out working perfectly if a post method called the follow codes
class MyCronJob(CronJobBase):
RUN_EVERY_MINS = 1 # every minute
schedule = Schedule(run_every_mins=RUN_EVERY_MINS)
code = 'Email_Test' # a unique code
def do(self):
print('######################################')
send_mail(
'Subject here from cron',
'Here is the message.',
'from#email.com',
['to#emailcom'],
fail_silently=False,
)
I tried running python manage.py runcrons and python manage.py runcrons --force and waited, (no errors, I also added the print because I want to see if the code even runs and good, I see the ################# got printed)
can someone please give me an advise?
Thanks in advance
Try a custom function for sending mail. I tried send_mail function and it did not work for me and i did not find any bug or explanation on this topic. Below is an example of sending html emails using smtplib.
import smtplib
def send_email(email, subject, message):
text = subject
html = message
msg = MIMEMultipart('alternative')
html_part = MIMEText(html, 'html')
text_part = MIMEText(text, 'plain')
msg.attach(text_part)
msg.attach(html_part)
msg['Subject'] = subject
msg['From'] = SENDER_EMAIL
msg['To'] = email
s = smtplib.SMTP(settings.GMAIL_SMTP)
s.ehlo()
s.starttls()
s.login(SENDER_EMAIL, SENDER_PASSWORD)
s.sendmail(SENDER_EMAIL, email, msg.as_string())
s.quit()
please make sure first you must have to import library from django.core.mail, to using send_mail. I know it's too late to help, but u can try this..
from django_cron import CronJobBase, Schedule
from django.core.mail import send_mail
class my_scheduled_job(CronJobBase):
RUN_EVERY_MINS = 1
schedule = Schedule(run_every_mins=RUN_EVERY_MINS)
code = 'user_dashboard.autoemail.my_scheduled_job'
def do(self):
subject= 'Send Email With Automatic Schedule'
message= 'Test send email :'
email_to= ['xxxxxxxx#gmail.com']
email_user(subject_test, message_test, email_to_test)
print ("done")
def email_user(subject, message, email_to):
email_from = 'noreply#xxxx.id'
send = send_mail(subject, message, email_from, email_to)
return send

Categories