def flush(from_test=False): """flush email queue, every time: called from scheduler""" # additional check check_email_limit([]) auto_commit = not from_test if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True frappe.db.sql("""update `tabEmail Queue` set status='Expired' where datediff(curdate(), creation) > 7 and status='Not Sent'""", auto_commit=auto_commit) smtpserver = SMTPServer() for i in xrange(500): # don't use for update here, as it leads deadlocks email = frappe.db.sql('''select * from `tabEmail Queue` where status='Not Sent' and (send_after is null or send_after < %(now)s) order by priority desc, creation asc limit 1''', { 'now': now_datetime() }, as_dict=True) if email: email = email[0] else: break send_one(email, smtpserver, auto_commit)
def send(email, append_to=None, retry=1): """send the message or add it to Outbox Email""" if frappe.flags.in_test: frappe.flags.sent_mail = email.as_string() return if frappe.are_emails_muted(): frappe.msgprint(_("Emails are muted")) return def _send(retry): try: smtpserver = SMTPServer(append_to=append_to) # validate is called in as_string email_body = email.as_string() smtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), email_body) except smtplib.SMTPSenderRefused: frappe.throw(_("Invalid login or password"), title='Email Failed') raise except smtplib.SMTPRecipientsRefused: frappe.msgprint(_("Invalid recipient address"), title='Email Failed') raise except (smtplib.SMTPServerDisconnected, smtplib.SMTPAuthenticationError): if not retry: raise else: retry = retry - 1 _send(retry) _send(retry)
def flush(from_test=False): """flush email queue, every time: called from scheduler""" smtpserver = SMTPServer() auto_commit = not from_test # additional check check_bulk_limit([]) if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True frappe.db.sql("""update `tabBulk Email` set status='Expired' where datediff(curdate(), creation) > 3 and status='Not Sent'""", auto_commit=auto_commit) for i in xrange(500): email = frappe.db.sql("""select * from `tabBulk Email` where status='Not Sent' and ifnull(send_after, "2000-01-01 00:00:00") < %s order by priority desc, creation asc limit 1 for update""", now_datetime(), as_dict=1) if email: email = email[0] else: break send_one(email, smtpserver, auto_commit)
def flush(from_test=False): """flush email queue, every time: called from scheduler""" # additional check auto_commit = not from_test if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True smtpserver_dict = frappe._dict() for email in get_queue(): if cint(frappe.defaults.get_defaults().get("hold_queue")) == 1: break if email.name: smtpserver = smtpserver_dict.get(email.sender) if not smtpserver: smtpserver = SMTPServer() smtpserver_dict[email.sender] = smtpserver if from_test: send_one(email.name, smtpserver, auto_commit) else: send_one_args = { 'email': email.name, 'smtpserver': smtpserver, 'auto_commit': auto_commit, } enqueue(method='frappe.email.queue.send_one', queue='short', **send_one_args)
def can_send_now(self): hold_queue = (cint( frappe.defaults.get_defaults().get("hold_queue")) == 1) if frappe.are_emails_muted() or not self.is_to_be_sent() or hold_queue: return False return True
def flush(from_test=False): """flush email queue, every time: called from scheduler""" # additional check check_bulk_limit([]) auto_commit = not from_test if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True frappe.db.sql("""update `tabBulk Email` set status='Expired' where datediff(curdate(), creation) > 7 and status='Not Sent'""", auto_commit=auto_commit) smtpserver = SMTPServer() for i in xrange(500): # don't use for update here, as it leads deadlocks email = frappe.db.sql('''select * from `tabBulk Email` where status='Not Sent' and (send_after is null or send_after < %(now)s) order by priority desc, creation asc limit 1''', { 'now': now_datetime() }, as_dict=True) if email: email = email[0] else: break send_one(email, smtpserver, auto_commit)
def send(email, append_to=None): """send the message or add it to Outbox Email""" if frappe.flags.in_test: frappe.flags.sent_mail = email.as_string() return if frappe.are_emails_muted(): frappe.msgprint(_("Emails are muted")) return try: smtpserver = SMTPServer(append_to=append_to) if hasattr(smtpserver, "always_use_account_email_id_as_sender") and \ cint(smtpserver.always_use_account_email_id_as_sender) and smtpserver.login: if not email.reply_to: email.reply_to = email.sender email.sender = smtpserver.login smtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), email.as_string()) except smtplib.SMTPSenderRefused: frappe.msgprint(_("Invalid login or password")) raise except smtplib.SMTPRecipientsRefused: frappe.msgprint(_("Invalid recipient address")) raise
def get_default_outgoing_email_account(raise_exception_not_set=True): email_account = _get_email_account({ "enable_outgoing": 1, "default_outgoing": 1 }) if not email_account and frappe.conf.get("mail_server"): # from site_config.json email_account = frappe.new_doc("Email Account") email_account.update({ "smtp_server": frappe.conf.get("mail_server"), "smtp_port": frappe.conf.get("mail_port"), "use_tls": cint(frappe.conf.get("use_ssl") or 0), "email_id": frappe.conf.get("mail_login"), "password": frappe.conf.get("mail_password"), "sender": frappe.conf.get("auto_email_id", "*****@*****.**") }) email_account.from_site_config = True email_account.name = frappe.conf.get("email_sender_name") or "Frappe" if not email_account and not raise_exception_not_set: return None if frappe.are_emails_muted(): # create a stub email_account = frappe.new_doc("Email Account") email_account.update({"sender": "*****@*****.**"}) return email_account
def get_default_outgoing_email_account(raise_exception_not_set=True): email_account = _get_email_account({"enable_outgoing": 1, "default_outgoing": 1}) if not email_account and frappe.conf.get("mail_server"): # from site_config.json email_account = frappe.new_doc("Email Account") email_account.update({ "smtp_server": frappe.conf.get("mail_server"), "smtp_port": frappe.conf.get("mail_port"), "use_tls": cint(frappe.conf.get("use_ssl") or 0), "email_id": frappe.conf.get("mail_login"), "password": frappe.conf.get("mail_password"), "sender": frappe.conf.get("auto_email_id", "*****@*****.**") }) email_account.from_site_config = True if not email_account and not raise_exception_not_set: return None if frappe.are_emails_muted(): # create a stub email_account = frappe.new_doc("Email Account") email_account.update({ "sender": "*****@*****.**" }) return email_account
def flush(from_test=False): """flush email queue, every time: called from scheduler""" smtpserver = SMTPServer() auto_commit = not from_test # additional check check_bulk_limit([]) if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True frappe.db.sql("""update `tabBulk Email` set status='Expired' where datediff(curdate(), creation) > 3""", auto_commit=auto_commit) for i in xrange(500): email = frappe.db.sql("""select * from `tabBulk Email` where status='Not Sent' and ifnull(send_after, "2000-01-01 00:00:00") < %s order by creation asc limit 1 for update""", now_datetime(), as_dict=1) if email: email = email[0] else: break frappe.db.sql( """update `tabBulk Email` set status='Sending' where name=%s""", (email["name"], ), auto_commit=auto_commit) try: if not from_test: smtpserver.setup_email_account(email.reference_doctype) smtpserver.sess.sendmail(email["sender"], email["recipient"], encode(email["message"])) frappe.db.sql( """update `tabBulk Email` set status='Sent' where name=%s""", (email["name"], ), auto_commit=auto_commit) except (smtplib.SMTPServerDisconnected, smtplib.SMTPConnectError, smtplib.SMTPHeloError, smtplib.SMTPAuthenticationError): # bad connection, retry later frappe.db.sql( """update `tabBulk Email` set status='Not Sent' where name=%s""", (email["name"], ), auto_commit=auto_commit) # no need to attempt further return except Exception, e: frappe.db.sql( """update `tabBulk Email` set status='Error', error=%s where name=%s""", (unicode(e), email["name"]), auto_commit=auto_commit)
def send(email, append_to=None): """send the message or add it to Outbox Email""" if frappe.flags.in_test: frappe.flags.sent_mail = email.as_string() return if frappe.are_emails_muted(): frappe.msgprint(_("Emails are muted")) return try: smtpserver = SMTPServer(append_to=append_to) # validate is called in as_string email_body = email.as_string() smtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), email_body) except smtplib.SMTPSenderRefused: frappe.msgprint(_("Invalid login or password")) raise except smtplib.SMTPRecipientsRefused: frappe.msgprint(_("Invalid recipient address")) raise
def get_default_outgoing_email_account(raise_exception_not_set=True): '''conf should be like: { "mail_server": "smtp.example.com", "mail_port": 587, "use_tls": 1, "mail_login": "******", "mail_password": "******", "auto_email_id": "*****@*****.**", "email_sender_name": "Example Notifications", "always_use_account_email_id_as_sender": 0, "always_use_account_name_as_sender_name": 0 } ''' email_account = _get_email_account({ "enable_outgoing": 1, "default_outgoing": 1 }) if email_account: email_account.password = email_account.get_password( raise_exception=False) if not email_account and frappe.conf.get("mail_server"): # from site_config.json email_account = frappe.new_doc("Email Account") email_account.update({ "smtp_server": frappe.conf.get("mail_server"), "smtp_port": frappe.conf.get("mail_port"), # legacy: use_ssl was used in site_config instead of use_tls, but meant the same thing "use_tls": cint(frappe.conf.get("use_tls") or 0) or cint(frappe.conf.get("use_ssl") or 0), "login_id": frappe.conf.get("mail_login"), "email_id": frappe.conf.get("auto_email_id") or frappe.conf.get("mail_login") or '*****@*****.**', "password": frappe.conf.get("mail_password"), "always_use_account_email_id_as_sender": frappe.conf.get("always_use_account_email_id_as_sender", 0), "always_use_account_name_as_sender_name": frappe.conf.get("always_use_account_name_as_sender_name", 0) }) email_account.from_site_config = True email_account.name = frappe.conf.get("email_sender_name") or "Frappe" if not email_account and not raise_exception_not_set: return None if frappe.are_emails_muted(): # create a stub email_account = frappe.new_doc("Email Account") email_account.update({"email_id": "*****@*****.**"}) return email_account
def flush(from_test=False): """flush email queue, every time: called from scheduler""" smtpserver = SMTPServer() auto_commit = not from_test # additional check check_bulk_limit([]) if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True frappe.db.sql("""update `tabBulk Email` set status='Expired' where datediff(curdate(), creation) > 3""", auto_commit=auto_commit) for i in xrange(100): email = frappe.db.sql("""select * from `tabBulk Email` where status='Not Sent' and ifnull(send_after, "2000-01-01 00:00:00") < %s order by priority desc, creation asc limit 1 for update""", now_datetime(), as_dict=1) if email: email = email[0] else: break frappe.db.sql("""update `tabBulk Email` set status='Sending' where name=%s""", (email["name"],), auto_commit=auto_commit) try: if not from_test: smtpserver.setup_email_account(email.reference_doctype) smtpserver.sess.sendmail(email["sender"], email["recipient"], encode(email["message"])) frappe.db.sql("""update `tabBulk Email` set status='Sent' where name=%s""", (email["name"],), auto_commit=auto_commit) except (smtplib.SMTPServerDisconnected, smtplib.SMTPConnectError, smtplib.SMTPHeloError, smtplib.SMTPAuthenticationError): # bad connection, retry later frappe.db.sql("""update `tabBulk Email` set status='Not Sent' where name=%s""", (email["name"],), auto_commit=auto_commit) # no need to attempt further return except Exception, e: frappe.db.sql("""update `tabBulk Email` set status='Error', error=%s where name=%s""", (unicode(e), email["name"]), auto_commit=auto_commit) finally:
def get_default_outgoing_email_account(raise_exception_not_set=True): '''conf should be like: { "mail_server": "smtp.example.com", "mail_port": 587, "use_tls": 1, "mail_login": "******", "mail_password": "******", "auto_email_id": "*****@*****.**", "email_sender_name": "Example Notifications", "always_use_account_email_id_as_sender": 0, "always_use_account_name_as_sender_name": 0 } ''' email_account = _get_email_account({"enable_outgoing": 1, "default_outgoing": 1}) if email_account: email_account.password = email_account.get_password(raise_exception=False) if not email_account and frappe.conf.get("mail_server"): # from site_config.json email_account = frappe.new_doc("Email Account") email_account.update({ "smtp_server": frappe.conf.get("mail_server"), "smtp_port": frappe.conf.get("mail_port"), # legacy: use_ssl was used in site_config instead of use_tls, but meant the same thing "use_tls": cint(frappe.conf.get("use_tls") or 0) or cint(frappe.conf.get("use_ssl") or 0), "login_id": frappe.conf.get("mail_login"), "email_id": frappe.conf.get("auto_email_id") or frappe.conf.get("mail_login") or '*****@*****.**', "password": frappe.conf.get("mail_password"), "always_use_account_email_id_as_sender": frappe.conf.get("always_use_account_email_id_as_sender", 0), "always_use_account_name_as_sender_name": frappe.conf.get("always_use_account_name_as_sender_name", 0) }) email_account.from_site_config = True email_account.name = frappe.conf.get("email_sender_name") or "Frappe" if not email_account and not raise_exception_not_set: return None if frappe.are_emails_muted(): # create a stub email_account = frappe.new_doc("Email Account") email_account.update({ "email_id": "*****@*****.**" }) return email_account
def flush(from_test=False): """flush email queue, every time: called from scheduler """ from frappe.email.doctype.email_queue.email_queue import send_mail # To avoid running jobs inside unit tests if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True if cint(frappe.defaults.get_defaults().get("hold_queue")) == 1: return for row in get_queue(): try: func = send_mail if from_test else send_mail.enqueue is_background_task = not from_test func(email_queue_name=row.name, is_background_task=is_background_task) except Exception: frappe.log_error()
def flush(from_test=False): """flush email queue, every time: called from scheduler""" # additional check cache = frappe.cache() check_email_limit([]) auto_commit = not from_test if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True smtpserver = SMTPServer() make_cache_queue() for i in xrange(cache.llen('cache_email_queue')): email = cache.lpop('cache_email_queue') if email: send_one(email, smtpserver, auto_commit)
def flush(from_test=False): """flush email queue, every time: called from scheduler""" # additional check cache = frappe.cache() check_email_limit([]) auto_commit = not from_test if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True smtpserver = SMTPServer() make_cache_queue() for i in xrange(cache.llen('cache_email_queue')): email = cache.lpop('cache_email_queue') if email: send_one(email, smtpserver, auto_commit, from_test=from_test)
def send(email, append_to=None): """send the message or add it to Outbox Email""" if frappe.flags.in_test: frappe.flags.sent_mail = email.as_string() return if frappe.are_emails_muted(): frappe.msgprint(_("Emails are muted")) return try: smtpserver = SMTPServer(append_to=append_to) smtpserver.replace_sender_in_email(email) smtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), email.as_string()) except smtplib.SMTPSenderRefused: frappe.msgprint(_("Invalid login or password")) raise except smtplib.SMTPRecipientsRefused: frappe.msgprint(_("Invalid recipient address")) raise
def flush(from_test=False): """flush email queue, every time: called from scheduler""" # additional check auto_commit = not from_test if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True smtpserver_dict = frappe._dict() for email in get_queue(): if cint(frappe.defaults.get_defaults().get("hold_queue")) == 1: break if email.name: smtpserver = smtpserver_dict.get(email.sender) if not smtpserver: smtpserver = SMTPServer() smtpserver_dict[email.sender] = smtpserver send_one(email.name, smtpserver, auto_commit, from_test=from_test)
def flush(from_test=False): """flush email queue, every time: called from scheduler""" # additional check check_email_limit([]) auto_commit = not from_test if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True smtpserver_dict = frappe._dict() for email in get_queue(): if cint(frappe.defaults.get_defaults().get("hold_queue"))==1: break if email.name: smtpserver = smtpserver_dict.get(email.sender) if not smtpserver: smtpserver = SMTPServer() smtpserver_dict[email.sender] = smtpserver send_one(email.name, smtpserver, auto_commit, from_test=from_test)
def send_one(email, smtpserver=None, auto_commit=True, now=False): """Send Email Queue with given smtpserver""" email = frappe.db.sql( """select name, status, communication, message, sender, reference_doctype, reference_name, unsubscribe_param, unsubscribe_method, expose_recipients, show_as_cc, add_unsubscribe_link, attachments, retry from `tabEmail Queue` where name=%s for update""", email, as_dict=True, ) if len(email): email = email[0] else: return recipients_list = frappe.db.sql( """select name, recipient, status from `tabEmail Queue Recipient` where parent=%s""", email.name, as_dict=1, ) if frappe.are_emails_muted(): frappe.msgprint(_("Emails are muted")) return if cint(frappe.defaults.get_defaults().get("hold_queue")) == 1: return if email.status not in ("Not Sent", "Partially Sent"): # rollback to release lock and return frappe.db.rollback() return frappe.db.sql( """update `tabEmail Queue` set status='Sending', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit, ) if email.communication: frappe.get_doc( "Communication", email.communication).set_delivery_status(commit=auto_commit) email_sent_to_any_recipient = None try: message = None if not frappe.flags.in_test: if not smtpserver: smtpserver = SMTPServer() # to avoid always using default email account for outgoing if getattr(frappe.local, "outgoing_email_account", None): frappe.local.outgoing_email_account = {} smtpserver.setup_email_account(email.reference_doctype, sender=email.sender) for recipient in recipients_list: if recipient.status != "Not Sent": continue message = prepare_message(email, recipient.recipient, recipients_list) if not frappe.flags.in_test: method = get_hook_method("override_email_send") if method: queue = frappe.get_doc("Email Queue", email.name) method(queue, email.sender, recipient.recipient, message) return else: smtpserver.sess.sendmail(email.sender, recipient.recipient, message) recipient.status = "Sent" frappe.db.sql( """update `tabEmail Queue Recipient` set status='Sent', modified=%s where name=%s""", (now_datetime(), recipient.name), auto_commit=auto_commit, ) email_sent_to_any_recipient = any("Sent" == s.status for s in recipients_list) # if all are sent set status if email_sent_to_any_recipient: frappe.db.sql( """update `tabEmail Queue` set status='Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit, ) else: frappe.db.sql( """update `tabEmail Queue` set status='Error', error=%s where name=%s""", ("No recipients to send to", email.name), auto_commit=auto_commit, ) if frappe.flags.in_test: frappe.flags.sent_mail = message return if email.communication: frappe.get_doc( "Communication", email.communication).set_delivery_status(commit=auto_commit) if smtpserver.append_emails_to_sent_folder and email_sent_to_any_recipient: smtpserver.email_account.append_email_to_sent_folder(message) except ( smtplib.SMTPServerDisconnected, smtplib.SMTPConnectError, smtplib.SMTPHeloError, smtplib.SMTPAuthenticationError, smtplib.SMTPRecipientsRefused, JobTimeoutException, ): # bad connection/timeout, retry later if email_sent_to_any_recipient: frappe.db.sql( """update `tabEmail Queue` set status='Partially Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit, ) else: frappe.db.sql( """update `tabEmail Queue` set status='Not Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit, ) if email.communication: frappe.get_doc( "Communication", email.communication).set_delivery_status(commit=auto_commit) # no need to attempt further return except Exception as e: frappe.db.rollback() if email.retry < 3: frappe.db.sql( """update `tabEmail Queue` set status='Not Sent', modified=%s, retry=retry+1 where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit, ) else: if email_sent_to_any_recipient: frappe.db.sql( """update `tabEmail Queue` set status='Partially Errored', error=%s where name=%s""", (text_type(e), email.name), auto_commit=auto_commit, ) else: frappe.db.sql( """update `tabEmail Queue` set status='Error', error=%s where name=%s""", (text_type(e), email.name), auto_commit=auto_commit, ) if email.communication: frappe.get_doc( "Communication", email.communication).set_delivery_status(commit=auto_commit) if now: print(frappe.get_traceback()) raise e else: # log to Error Log frappe.log_error("frappe.email.queue.flush")
def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=False): '''Send Email Queue with given smtpserver''' email = frappe.db.sql('''select name, status, communication, message, sender, reference_doctype, reference_name, unsubscribe_param, unsubscribe_method, expose_recipients, show_as_cc, add_unsubscribe_link, attachments from `tabEmail Queue` where name=%s for update''', email, as_dict=True)[0] recipients_list = frappe.db.sql('''select name, recipient, status from `tabEmail Queue Recipient` where parent=%s''', email.name, as_dict=1) if frappe.are_emails_muted(): frappe.msgprint(_("Emails are muted")) return if cint(frappe.defaults.get_defaults().get("hold_queue")) == 1: return if email.status not in ('Not Sent', 'Partially Sent'): # rollback to release lock and return frappe.db.rollback() return frappe.db.sql( """update `tabEmail Queue` set status='Sending', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) if email.communication: frappe.get_doc( 'Communication', email.communication).set_delivery_status(commit=auto_commit) try: if not frappe.flags.in_test: if not smtpserver: smtpserver = SMTPServer() smtpserver.setup_email_account(email.reference_doctype, sender=email.sender) for recipient in recipients_list: if recipient.status != "Not Sent": continue message = prepare_message(email, recipient.recipient, recipients_list) if not frappe.flags.in_test: smtpserver.sess.sendmail(email.sender, recipient.recipient, encode(message)) recipient.status = "Sent" frappe.db.sql( """update `tabEmail Queue Recipient` set status='Sent', modified=%s where name=%s""", (now_datetime(), recipient.name), auto_commit=auto_commit) #if all are sent set status if any("Sent" == s.status for s in recipients_list): frappe.db.sql( """update `tabEmail Queue` set status='Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) else: frappe.db.sql( """update `tabEmail Queue` set status='Error', error=%s where name=%s""", ("No recipients to send to", email.name), auto_commit=auto_commit) if frappe.flags.in_test: frappe.flags.sent_mail = message return if email.communication: frappe.get_doc( 'Communication', email.communication).set_delivery_status(commit=auto_commit) except (smtplib.SMTPServerDisconnected, smtplib.SMTPConnectError, smtplib.SMTPHeloError, smtplib.SMTPAuthenticationError, JobTimeoutException): # bad connection/timeout, retry later if any("Sent" == s.status for s in recipients_list): frappe.db.sql( """update `tabEmail Queue` set status='Partially Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) else: frappe.db.sql( """update `tabEmail Queue` set status='Not Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) if email.communication: frappe.get_doc( 'Communication', email.communication).set_delivery_status(commit=auto_commit) # no need to attempt further return except Exception as e: frappe.db.rollback() if any("Sent" == s.status for s in recipients_list): frappe.db.sql( """update `tabEmail Queue` set status='Partially Errored', error=%s where name=%s""", (text_type(e), email.name), auto_commit=auto_commit) else: frappe.db.sql( """update `tabEmail Queue` set status='Error', error=%s where name=%s""", (text_type(e), email.name), auto_commit=auto_commit) if email.communication: frappe.get_doc( 'Communication', email.communication).set_delivery_status(commit=auto_commit) if now: print(frappe.get_traceback()) raise e else: # log to Error Log log('frappe.email.queue.flush', text_type(e))
def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=False): '''Send Email Queue with given smtpserver''' email = frappe.db.sql('''select name, status, communication, message, sender, reference_doctype, reference_name, unsubscribe_param, unsubscribe_method, expose_recipients, show_as_cc, add_unsubscribe_link, attachments from `tabEmail Queue` where name=%s for update''', email, as_dict=True)[0] recipients_list = frappe.db.sql('''select name, recipient, status from `tabEmail Queue Recipient` where parent=%s''',email.name,as_dict=1) if frappe.are_emails_muted(): frappe.msgprint(_("Emails are muted")) return if cint(frappe.defaults.get_defaults().get("hold_queue"))==1 : return if email.status not in ('Not Sent','Partially Sent') : # rollback to release lock and return frappe.db.rollback() return frappe.db.sql("""update `tabEmail Queue` set status='Sending', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) if email.communication: frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) try: if not frappe.flags.in_test: if not smtpserver: smtpserver = SMTPServer() smtpserver.setup_email_account(email.reference_doctype, sender=email.sender) for recipient in recipients_list: if recipient.status != "Not Sent": continue message = prepare_message(email, recipient.recipient, recipients_list) if not frappe.flags.in_test: smtpserver.sess.sendmail(email.sender, recipient.recipient, encode(message)) recipient.status = "Sent" frappe.db.sql("""update `tabEmail Queue Recipient` set status='Sent', modified=%s where name=%s""", (now_datetime(), recipient.name), auto_commit=auto_commit) #if all are sent set status if any("Sent" == s.status for s in recipients_list): frappe.db.sql("""update `tabEmail Queue` set status='Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) else: frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s where name=%s""", ("No recipients to send to", email.name), auto_commit=auto_commit) if frappe.flags.in_test: frappe.flags.sent_mail = message return if email.communication: frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) except (smtplib.SMTPServerDisconnected, smtplib.SMTPConnectError, smtplib.SMTPHeloError, smtplib.SMTPAuthenticationError, JobTimeoutException): # bad connection/timeout, retry later if any("Sent" == s.status for s in recipients_list): frappe.db.sql("""update `tabEmail Queue` set status='Partially Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) else: frappe.db.sql("""update `tabEmail Queue` set status='Not Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) if email.communication: frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) # no need to attempt further return except Exception as e: frappe.db.rollback() if any("Sent" == s.status for s in recipients_list): frappe.db.sql("""update `tabEmail Queue` set status='Partially Errored', error=%s where name=%s""", (text_type(e), email.name), auto_commit=auto_commit) else: frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s where name=%s""", (text_type(e), email.name), auto_commit=auto_commit) if email.communication: frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit) if now: print(frappe.get_traceback()) raise e else: # log to Error Log log('frappe.email.queue.flush', text_type(e))