def contact(request): captcha = None show_captcha = settings.REGISTRATION_CAPTCHA and not request.user.is_authenticated if request.method == "POST": form = ContactForm(request.POST) if show_captcha: captcha = CaptchaForm(request, form, request.POST) if not check_rate_limit("message", request): messages.error( request, _("Too many messages sent, please try again later.")) elif (captcha is None or captcha.is_valid()) and form.is_valid(): mail_admins_contact( request, "%(subject)s", CONTACT_TEMPLATE, form.cleaned_data, str( Address( display_name=form.cleaned_data["name"], addr_spec=form.cleaned_data["email"], )), settings.ADMINS_CONTACT, ) return redirect("home") else: initial = get_initial_contact(request) if request.GET.get("t") in CONTACT_SUBJECTS: initial["subject"] = CONTACT_SUBJECTS[request.GET["t"]] form = ContactForm(initial=initial) if show_captcha: captcha = CaptchaForm(request) return render( request, "accounts/contact.html", { "form": form, "captcha_form": captcha, "title": _("Contact") }, )
def sendMail(HTML_MESSAGE, display_name, username, domain, title): #Gmail mailbox details email_address = '*****@*****.**' #gmail mailbox email_password = '******' #password to the mailbox #Define recipent to_address = (Address(display_name, username, domain), ) #Create message with defined from/to addresss, subject and text msg = create_email_message( from_address=email_address, to_address=to_address, subject=title, plaintext="Plain text version.", #plain e-mail text html=HTML_MESSAGE) #html e-mail text #Connect to Gmail mail server and send message with smtplib.SMTP('smtp.gmail.com', port=587) as smtp_server: smtp_server.ehlo() smtp_server.starttls() smtp_server.login(email_address, email_password) smtp_server.send_message(msg) print('Email sent successfully')
def draft_acceptance_email(proposal: Proposal): # Recipent username, domain = proposal.email.split("@") to_address = (Address(display_name=proposal.name, username=username, domain=domain), ) return create_email_message( from_address=EMAIL_ADDRESS, to_address=to_address, subject="Chicago Python presents Spooky Lightning Talk Status", body=f"""Hi {proposal.name.split(" ")[0]}, Congratulations! Your submitted talk, {proposal.talk_title}, was accepted for Spooky Lightning Talks night on Wednesday, October 30th. Please reply to this email to confirm you will be able to present your 5 minute talk. I have secured equipment and will be recording each session for upload to the ChiPy YouTube channel. Best Regards, Aly Sivji""")
def get_email_for_user(self, userid): tries = 2 while tries: try: userinfo = self._get_userinfo(userid) app_log.debug('Userinfo for %s: %s', userid, userinfo) if not userinfo.get('email', '').strip(): raise Exception('No email configured for user %s', userid) return Address(display_name=userinfo.get('display_name', ''), addr_spec=userinfo['email']) except InvalidGrantError as e: # log this error app_log.error("Invalid Grant Error %s", e) self._fetch_token() tries -= 1 except TokenExpiredError as e: # our refreshtoken is gone :( app_log.error("Token Expired Error %s", e) self._fetch_token() tries -= 1
def default_from_email(self) -> str: sender_name: str = self.default_mail_sender_name sender_address: Optional[str] = self.default_mail_sender_address if not sender_address: sender_address = settings.DEFAULT_FROM_EMAIL if not sender_address: raise ImproperlyConfigured( "No sender email address has been set-up") sender_name, sender_address = parseaddr(sender_address) # Note: we only want to format the address in accordance to RFC 5322 # but our job is not to sanitize the values. The sanitized value, encoding, etc. # will depend on the email backend being used. # # Refer to email.header.Header and django.core.mail.message.sanitize_address. value = str(Address(sender_name, addr_spec=sender_address)) return value
def ses_relay_email(from_address, relay_address, subject, message_body, attachments): relay_from_address, relay_from_display = generate_relay_From(from_address) formatted_from_address = str( Address(relay_from_display, addr_spec=relay_from_address)) try: if attachments: response = ses_send_raw_email(formatted_from_address, relay_address.user.email, subject, message_body, attachments) else: response = ses_send_email(formatted_from_address, relay_address.user.email, subject, message_body) relay_address.num_forwarded += 1 relay_address.last_used_at = datetime.now(timezone.utc) relay_address.save(update_fields=['num_forwarded', 'last_used_at']) return response except ClientError as e: logger.error('ses_client_error', extra=e.response['Error']) return HttpResponse("SES client error", status=400)
def send(body): # Gmail details email_address = os.environ.get("EMAIL_ADDRESSE") email_password = os.environ.get("PASSWORD") # Recipent to_address = (Address(display_name='naim', username='******', domain='gmail.com'), ) if __name__ == '__main__': msg = create_email_message(from_address=email_address, to_address=to_address, subject='Hello World', body=body) with smtplib.SMTP('smtp.gmail.com', port=587) as smtp_server: smtp_server.ehlo() smtp_server.starttls() smtp_server.login(email_address, email_password) smtp_server.send_message(msg)
def generate_relay_From(original_from_address): relay_display_name, relay_from_address = parseaddr( settings.RELAY_FROM_ADDRESS) # RFC 2822 (https://tools.ietf.org/html/rfc2822#section-2.1.1) # says email header lines must not be more than 998 chars long. # Encoding display names to longer than 998 chars will add wrap # characters which are unsafe. (See https://bugs.python.org/issue39073) # So, truncate the original sender to 900 chars so we can add our # "[via Relay] <relayfrom>" and encode it all. if len(original_from_address) > 998: original_from_address = '%s ...' % original_from_address[:900] # line breaks in From: will encode to unsafe chars, so strip them. original_from_address = (original_from_address.replace( '\u2028', '').replace('\r', '').replace('\n', '')) display_name = Header('"%s [via Relay]"' % (original_from_address), 'UTF-8') formatted_from_address = str( Address(display_name.encode(maxlinelen=998), addr_spec=relay_from_address)) return formatted_from_address
def prep_mail(self, current_channel, what, username, cloak, message): current_time = time.strftime('%Y-%m-%d %I:%M:%S%p', time.localtime()) msg = EmailMessage() msg['Subject'] = '[ALERT: IRC]' msg['From'] = sender msg['To'] = [ Address(destination[email], email.split('@')[0], email.split('@')[1]) for email in destination ] # Not sure if this is actually needed since we're using add_alternative # https://docs.python.org/3/library/email.message.html#email.message.EmailMessage.add_alternative msg.set_content = '' if not jinja_found: self.email_template = NO_JINJA_EMAIL_TEMPLATE if current_channel is not None: self.email_template += f" <b>Current Channel</b>: {current_channel}</br>\n" if what is not None: self.email_template += f" <b>Type</b>: {what}</br>\n" if username is not None: self.email_template += f" <b>Username</b>: {{username}}</br>\n" if cloak is not None: self.email_template += f" <b>Cloak</b>: {{cloak}}</br>\n" if message is not None: self.email_template += f" <b>Message</b>: {message}\n" self.email_template += " <body>\n" + "</html>" msg.add_alternative(self.email_template, subtype='html') else: self.email_template = JINJA_EMAIL_TEMPLATE data = { 'current_time': current_time, 'current_channel': current_channel, 'what': what, 'username': username, 'cloak': cloak, 'message': message } prep_template = Template(self.email_template) msg.add_alternative(prep_template.render(data), subtype='html') self.mail_this(msg)
def send_email(config: EmailConfig, recipient_list, context, subject="", template_str=""): sender_name = config.sender_name or "" sender_address = config.sender_address from_email = str(Address(sender_name, addr_spec=sender_address)) email_backend = EmailBackend( host=config.host, port=config.port, username=config.username, password=config.password, use_ssl=config.use_ssl, use_tls=config.use_tls, timeout=DEFAULT_EMAIL_TIMEOUT, ) compiler = pybars.Compiler() template = compiler.compile(template_str) subject_template = compiler.compile(subject) helpers = { "format_address": format_address, "price": price, "format_datetime": format_datetime, "get_product_image_thumbnail": get_product_image_thumbnail, "compare": compare, } message = template(context, helpers=helpers) subject_message = subject_template(context, helpers) send_mail( subject_message, html2text.html2text(message), from_email, recipient_list, html_message=message, connection=email_backend, )
def draft_acceptance_email(proposal: Proposal): # Recipent username, domain = proposal.email.split("@") to_address = (Address(display_name=proposal.name, username=username, domain=domain), ) return create_email_message( from_address=EMAIL_ADDRESS, to_address=to_address, subject="Python Powered Healthcare: Talk Accepted", body=f"""Hi {proposal.name.split(" ")[0]}, Congratulations! Your submitted talk, {proposal.talk_title}, was accepted for Python Powered Healthcare Night on Wednesday, February 19th. Please reply to this email to confirm you will be able to attend and present your lightning talk. Details about the event can be found on https://www.meetup.com/_ChiPy_/events/267264866/. Apologies if your talk description was shortened. Meetup has a character limit for the event description. Please make sure to RSVP to ensure we have an accurate headcount. Best Regards, Aly Sivji""")
def sanitize_address(addr, encoding): """ Format a pair of (name, address) or an email address string. """ address = None if not isinstance(addr, tuple): addr = force_str(addr) try: token, rest = parser.get_mailbox(addr) except (HeaderParseError, ValueError, IndexError): raise ValueError('Invalid address "%s"' % addr) else: if rest: # The entire email address must be parsed. raise ValueError( 'Invalid adddress; only %s could be parsed from "%s"' % (token, addr) ) nm = token.display_name or '' localpart = token.local_part domain = token.domain or '' else: nm, address = addr localpart, domain = address.rsplit('@', 1) nm = Header(nm, encoding).encode() # Avoid UTF-8 encode, if it's possible. try: localpart.encode('ascii') except UnicodeEncodeError: localpart = Header(localpart, encoding).encode() try: domain.encode('ascii') except UnicodeEncodeError: domain = domain.encode('idna').decode('ascii') parsed_address = Address(nm, username=localpart, domain=domain) return str(parsed_address)
def send(temp, humid, last_time): msg = EmailMessage() msg['Subject'] = "Automated temperature alert" msg['From'] = Address("RPI-server", "humitemp", mail_user) msg['To'] = (mail_receiver) msg.set_content("""\ <html> <head></head> <body> <p>temperature is {:0.1f}, optimally you want to stay under 26c<p/> <p>humidity is {:0.1f}, higher temps benefit from higher humidity<p/> </body> </html> """.format(temp, humid, config.CNF['last_time_called'], time.time(), (time.time() - config.CNF['last_time_called'])), subtype='html') with smtplib.SMTP('smtp.gmail.com', 587) as smtp: smtp.ehlo() smtp.starttls() smtp.ehlo() smtp.login(user=mail_user, password=mail_password) smtp.send_message(msg)
def validate_email(self, field): # Additional checks for the validity of the address try: Address(addr_spec=field.data) except ValueError: raise wtforms.validators.ValidationError( _("The email address isn't valid. Try again.") ) # Check if the domain is valid domain = field.data.split("@")[-1] if domain in disposable_email_domains.blacklist: raise wtforms.validators.ValidationError( _( "You can't use an email address from this domain. Use a " "different email." ) ) # Check if this email address is already in use userid = self.user_service.find_userid_by_email(field.data) if userid and userid == self.user_id: raise wtforms.validators.ValidationError( _( "This email address is already being used by this account. " "Use a different email." ) ) if userid: raise wtforms.validators.ValidationError( _( "This email address is already being used " "by another account. Use a different email." ) )
def send(self): smtp = smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) smtp.ehlo_or_helo_if_needed() smtp.login(self.username, self.password) msg = EmailMessage() msg['subject'] = self.subject msg['from'] = Address(self.display_name, addr_spec=self.username) msg['to'] = ', '.join(self.receivers) if self.cc: msg['cc'] = ','.join(self.cc) msg.add_alternative(self.content, subtype='html') for file in self.files: if not os.path.isfile(file): continue ctype, encoding = mimetypes.guess_type(file) if ctype is None or encoding is not None: # No guess could be made, or the file is encoded (compressed), so # use a generic bag-of-bits type. ctype = 'application/octet-stream' maintype, subtype = ctype.split('/', 1) with open(file, 'rb') as fp: msg.add_attachment(fp.read(), maintype=maintype, subtype=subtype, filename=file) for image in self.images: if not os.path.isfile(image): print('not found:', image) continue with open(image, 'rb') as fp: image_type = imghdr.what(image) msg.add_attachment(fp.read(), 'image', image_type, cid=image) smtp.send_message(msg) smtp.quit() return
def send(subject: str, send_list: list, body: str, from_email: str = None) -> bool: """ Function to send email with a gmail account to use this function you need activate the follow permission https://myaccount.google.com/lesssecureapps?pli=1 on gmail account. :param subject: email subject :param from_email: from email, default None :param send_list: email or list of emails to send :param body: email body. :return: """ # Make destination list destinations = [] for email in send_list: info = email['email'].split('@') assert len(info) == 2, "Email incorrect verify list" add = Address(display_name=email['name'], username=info[0], domain=info[1]) destinations.append(add) # Login account G_MAIL.login(G_EMAIL, PASSWORD) # CONFIGURING EMAIL MESSAGE = EmailMessage() MESSAGE['Subject'] = subject MESSAGE['From'] = G_EMAIL MESSAGE.set_content(body) MESSAGE['To'] = tuple(destinations) if len(destinations) > 1 else destinations[0] # Send message G_MAIL.send_message(MESSAGE) # Close connection G_MAIL.quit() return True
def test_extract_recipients_resent_message(): message = EmailMessage() message["To"] = Address(username="******", domain="example.com") message["Cc"] = Address(username="******", domain="example.com") message["Bcc"] = Address(username="******", domain="example.com") message["Resent-Date"] = "Mon, 20 Nov 2017 21:04:27 -0000" message["Resent-To"] = Address(username="******", domain="example.com") message["Resent-Cc"] = Address(username="******", domain="example.com") message["Resent-Bcc"] = Address(username="******", domain="example.com") recipients = extract_recipients(message) assert message["Resent-To"] in recipients assert message["Resent-Cc"] in recipients assert message["Resent-Bcc"] in recipients assert message["To"] not in recipients assert message["Cc"] not in recipients assert message["Bcc"] not in recipients
def email(self, recipients, subject=None, should_email=True, should_message_slack=True, should_attach_log=True, should_escape_chars=True): if not subject: assert isinstance(self.title, str) or self.pipeline is not None, "either title or pipeline must be specified when subject is missing" subject = self.title if self.title else self.pipeline.title smtp = { "host": Config.get("email.smtp.host", None), "port": Config.get("email.smtp.port", None), "user": Config.get("email.smtp.user", None), "pass": Config.get("email.smtp.pass", None) } sender = Address(Config.get("email.sender.name", "undefined"), addr_spec=Config.get("email.sender.address", "*****@*****.**")) # 0. Create attachment with complete log (including DEBUG statements) if should_attach_log is True: self.attachLog() attachments = [] for m in self._messages["attachment"]: smb, file, unc = Filesystem.networkpath(m["text"]) base_path = Filesystem.get_base_path(m["text"], self.pipeline.dir_base) relpath = os.path.relpath(m["text"], base_path) if base_path else None if m["text"].startswith(self.reportDir()): relpath = os.path.relpath(m["text"], self.reportDir()) if not [a for a in attachments if a["unc"] == unc]: attachments.append({ "title": "{}{}".format(relpath, ("/" if os.path.isdir(m["text"]) else "")), "smb": smb, "file": file, "unc": unc, "severity": m["severity"] }) # Determine overall status status = "INFO" for message_type in self._messages: for m in self._messages[message_type]: if m["severity"] == "SUCCESS" and status in ["INFO"]: status = "SUCCESS" elif m["severity"] == "WARN" and status in ["INFO", "SUCCESS"]: status = "WARN" elif m["severity"] == "ERROR": status = "ERROR" try: assert isinstance(smtp, dict), "smtp must be a dict" assert isinstance(sender, Address), "sender must be a Address" assert isinstance(recipients, str) or isinstance(recipients, list) or isinstance(recipients, tuple), "recipients must be a str, list or tuple" assert isinstance(self.title, str) or self.pipeline and isinstance(self.pipeline.title, str), "title or pipeline.title must be a str" if isinstance(recipients, str): recipients = [recipients] elif isinstance(recipients, tuple): recipients = list(recipients) if status == "ERROR": for key in Config.get("administrators", default=[]): if key not in recipients: recipients.append(key) # when testing, only allow e-mail addresses defined in the ALLOWED_EMAIL_ADDRESSES_IN_TEST env var if Config.get("test"): subject = "[test] " + subject filtered_recipients = [] for recipient in recipients: if recipient in Config.get("email.allowed_email_addresses_in_test"): filtered_recipients.append(recipient) recipients = filtered_recipients # 1. join lines with severity SUCCESS/INFO/WARN/ERROR markdown_text = [] for m in self._messages["message"]: if should_escape_chars: text = m['text'].replace("&", "&").replace("<", "<").replace(">", ">") else: text = m['text'] if m['preformatted'] is True: markdown_text.append("<pre>{}</pre>".format(text)) elif m['severity'] != 'DEBUG': markdown_text.append(text) if attachments != [] or should_attach_log is True: markdown_text.append("\n----\n") markdown_text.append("\n# Lenker\n") markdown_text.append("\n<ul style=\"list-style: none;\">") # Pick icon and style for INFO-attachments attachment_styles = { "DEBUG": { "icon": "🗎", "style": "" }, "INFO": { "icon": "🛈", "style": "" }, "SUCCESS": { "icon": "😄", "style": "background-color: #bfffbf;" }, "WARN": { "icon": "😟", "style": "background-color: #ffffbf;" }, "ERROR": { "icon": "ðŸ˜", "style": "background-color: #ffbfbf;" } } for attachment in attachments: # UNC links seems to be preserved when viewed in Outlook. # file: and smb: URIs are disallowed or removed. # So these links will only work in Windows. # If we need this to work cross-platform, we would have # to map the network share paths to a web server so that # the transfers go through http:. This could maybe be mapped # using environment variables. li = "<li>" li += "<span style=\"vertical-align: middle; font-size: 200%;\">" + attachment_styles[attachment["severity"]]["icon"] + "</span> " li += "<span style=\"vertical-align: middle; " + attachment_styles[attachment["severity"]]["style"] + "\">" li += "<a href=\"file:///" + attachment["unc"] + "\">" + attachment["title"] + "</a> " li += "<a href=\"" + attachment["smb"] + "\">" + self.img_string + "=\" alt=\"" + attachment["smb"] + "\"/>" + "</a> " li += "</span>" li += "</li>" markdown_text.append(li) markdown_text.append("</ul>\n") label_string = "" for label in self.pipeline.labels: label_string += "[{}] ".format(label) markdown_text.append("\n[{}] {} [{}] [status:{}]".format(self.pipeline.uid, label_string, self.pipeline.publication_format, status)) markdown_text = "\n".join(markdown_text) # 2. parse string as Markdown and render as HTML if should_escape_chars: markdown_html = markdown.markdown(markdown_text, extensions=['markdown.extensions.fenced_code', 'markdown.extensions.codehilite']) else: markdown_html = markdown_text markdown_html = '''<!DOCTYPE html> <html> <head> <meta charset=\"utf-8\"/> <title>''' + subject.replace("&", "&").replace("<", "<").replace(">", ">") + '''</title> </head> <body> ''' + markdown_html + ''' </body> </html> ''' if not should_email: logging.info("[e-mail] Not sending email") else: # 3. build e-mail msg = EmailMessage() msg['Subject'] = re.sub(r"\s", " ", subject).strip() msg['From'] = sender msg['To'] = Report.emailStringsToAddresses(recipients) msg.set_content(markdown_text) msg.add_alternative(markdown_html, subtype="html") logging.info("[e-mail] E-mail with subject '{}' will be sent to: {}".format(msg['Subject'], ", ".join(recipients))) # 4. send e-mail if smtp["host"] and smtp["port"]: smtp_server = "{}:{}".format(smtp["host"], smtp["port"]) logging.info("[e-mail] SMTP server: {}".format(smtp_server)) with smtplib.SMTP(smtp_server) as s: s.ehlo() # s.starttls() if smtp["user"] and smtp["pass"]: s.login(smtp["user"], smtp["pass"]) else: logging.debug("[e-mail] user/pass not configured") logging.debug("[e-mail] sending…") s.send_message(msg) logging.debug("[e-mail] sending complete.") else: logging.warning("[e-mail] host/port not configured") temp_md_obj = tempfile.NamedTemporaryFile(suffix=".md") temp_html_obj = tempfile.NamedTemporaryFile(suffix=".html") with open(temp_md_obj.name, "w") as f: f.write(markdown_text) logging.debug("[e-mail] markdown: {}".format(temp_md_obj.name)) with open(temp_html_obj.name, "w") as f: f.write(markdown_html) logging.debug("[e-mail] html: {}".format(temp_html_obj.name)) if should_attach_log is True: path_mail = os.path.join(self.reportDir(), "email.html") shutil.copy(temp_html_obj.name, path_mail) self.mailpath = Filesystem.networkpath(path_mail) else: yesterday = datetime.now() - timedelta(1) yesterday = str(yesterday.strftime("%Y-%m-%d")) path_mail = os.path.join(self.pipeline.dir_reports, "logs", "dagsrapporter", yesterday, self.pipeline.uid + ".html") shutil.copy(temp_html_obj.name, path_mail) self.mailpath = Filesystem.networkpath(path_mail) except AssertionError as e: logging.error("[e-mail] " + str(e)) if not should_message_slack: logging.warning("Not sending message to slack") else: # 5. send message to Slack slack_attachments = [] for attachment in attachments: color = None if attachment["severity"] == "SUCCESS": color = "good" elif attachment["severity"] == "WARN": color = "warning" elif attachment["severity"] == "ERROR": color = "danger" slack_attachments.append({ "title_link": attachment["smb"], "title": attachment["title"], "fallback": attachment["title"], "color": color }) Slack.slack(text=subject, attachments=slack_attachments)
from django.core.mail import send_mail from django.template.loader import render_to_string from django.test import override_settings, RequestFactory from email.headerregistry import Address organizers = Address("Outreachy Organizers", "organizers", "outreachy.org") def send_template_mail(template, context, recipient_list, request=None, **kwargs): for recipient in recipient_list: context['recipient'] = recipient message = render_to_string(template, context, request=request, using='plaintext').strip() subject, body = message.split('\n', 1) kwargs.setdefault('from_email', organizers) send_mail(message=body.strip(), subject=subject.strip(), recipient_list=[recipient], **kwargs) def approval_status_changed(obj, request): if obj.approval_status == obj.PENDING: recipients = obj.get_approver_email_list() elif obj.approval_status == obj.APPROVED:
from django.core.mail import send_mail from django.core.signing import TimestampSigner from django.template.loader import render_to_string from django.test import override_settings, RequestFactory from email.headerregistry import Address organizers = Address("Outreachy Organizers", "organizers", "outreachy.org") applicant_help = Address("Outreachy Applicant Helpers", "applicant-help", "outreachy.org") def send_template_mail(template, context, recipient_list, request=None, **kwargs): for recipient in recipient_list: context['recipient'] = recipient message = render_to_string(template, context, request=request, using='plaintext').strip() subject, body = message.split('\n', 1) if 'initial application' in subject: kwargs.setdefault('from_email', applicant_help) else: kwargs.setdefault('from_email', organizers) send_mail(message=body.strip(), subject=subject.strip(), recipient_list=[recipient], **kwargs) def send_group_template_mail(template, context, recipient_list, request=None, **kwargs): context['recipient'] = recipient_list message = render_to_string(template, context, request=request, using='plaintext').strip() subject, body = message.split('\n', 1) kwargs.setdefault('from_email', organizers) send_mail(message=body.strip(), subject=subject.strip(), recipient_list=recipient_list, **kwargs) def applicant_approval_status_changed(obj, request): if obj.approval_status == obj.PENDING: recipients = obj.get_approver_email_list() elif obj.approval_status == obj.APPROVED:
text_results += f" {size['name']:>11}: {dl_status[size['status']]}\n" html_results += f" <tr><td>{size['name']}</td><td><font color=\"{link_color[size['status']]}\">" \ f"{dl_status[size['status']]}</font></td></tr>\n" html_results += " </table>\n" text_results += "\n\n" html_results += " </body>\n</html>\n" # Build email, and send if config['mail']['send'] and got_new: envelope = EmailMessage() (from_name, from_user, from_domain) = config['mail']['from'].split(",") to_tuple = () for recp in config['mail']['to'].split(":"): (to_name, to_user, to_domain) = recp.split(",") to_tuple = to_tuple + (Address(to_name, to_user, to_domain), ) envelope['Subject'] = "New Images downloaded from Digital Blasphemy" envelope['From'] = Address(from_name, from_user, from_domain) envelope['To'] = to_tuple envelope.set_content(text_results) envelope.add_alternative(html_results, subtype="html") server = smtplib.SMTP(config['mail']['server'], config['mail']['port']) server.local_hostname = "db-downloader" server.ehlo_or_helo_if_needed() if "starttls" in server.esmtp_features: server.starttls() if config['mail']['auth_req']: try: server.login(config['mail']['user'], config['mail']['pass']) except smtplib.SMTPAuthenticationError as err:
def do_send_missedmessage_events_reply_in_zulip(user_profile: UserProfile, missed_messages: List[Dict[ str, Any]], message_count: int) -> None: """ Send a reminder email to a user if she's missed some PMs by being offline. The email will have its reply to address set to a limited used email address that will send a Zulip message to the correct recipient. This allows the user to respond to missed PMs, huddles, and @-mentions directly from the email. `user_profile` is the user to send the reminder to `missed_messages` is a list of dictionaries to Message objects and other data for a group of messages that share a recipient (and topic) """ from zerver.context_processors import common_context # Disabled missedmessage emails internally if not user_profile.enable_offline_email_notifications: return recipients = {(msg["message"].recipient_id, msg["message"].topic_name()) for msg in missed_messages} if len(recipients) != 1: raise ValueError( f"All missed_messages must have the same recipient and topic {recipients!r}", ) # This link is no longer a part of the email, but keeping the code in case # we find a clean way to add it back in the future unsubscribe_link = one_click_unsubscribe_link(user_profile, "missed_messages") context = common_context(user_profile) context.update( name=user_profile.full_name, message_count=message_count, unsubscribe_link=unsubscribe_link, realm_name_in_notifications=user_profile.realm_name_in_notifications, ) triggers = [message["trigger"] for message in missed_messages] unique_triggers = set(triggers) context.update( mention="mentioned" in unique_triggers or "wildcard_mentioned" in unique_triggers, stream_email_notify="stream_email_notify" in unique_triggers, mention_count=triggers.count("mentioned") + triggers.count("wildcard_mentioned"), ) # If this setting (email mirroring integration) is enabled, only then # can users reply to email to send message to Zulip. Thus, one must # ensure to display warning in the template. if settings.EMAIL_GATEWAY_PATTERN: context.update(reply_to_zulip=True, ) else: context.update(reply_to_zulip=False, ) from zerver.lib.email_mirror import create_missed_message_address reply_to_address = create_missed_message_address( user_profile, missed_messages[0]["message"]) if reply_to_address == FromAddress.NOREPLY: reply_to_name = "" else: reply_to_name = "Zulip" narrow_url = get_narrow_url(user_profile, missed_messages[0]["message"]) context.update(narrow_url=narrow_url, ) senders = list({m["message"].sender for m in missed_messages}) if missed_messages[0]["message"].recipient.type == Recipient.HUDDLE: display_recipient = get_display_recipient( missed_messages[0]["message"].recipient) # Make sure that this is a list of strings, not a string. assert not isinstance(display_recipient, str) other_recipients = [ r["full_name"] for r in display_recipient if r["id"] != user_profile.id ] context.update(group_pm=True) if len(other_recipients) == 2: huddle_display_name = " and ".join(other_recipients) context.update(huddle_display_name=huddle_display_name) elif len(other_recipients) == 3: huddle_display_name = ( f"{other_recipients[0]}, {other_recipients[1]}, and {other_recipients[2]}" ) context.update(huddle_display_name=huddle_display_name) else: huddle_display_name = "{}, and {} others".format( ", ".join(other_recipients[:2]), len(other_recipients) - 2) context.update(huddle_display_name=huddle_display_name) elif missed_messages[0]["message"].recipient.type == Recipient.PERSONAL: context.update(private_message=True) elif context["mention"] or context["stream_email_notify"]: # Keep only the senders who actually mentioned the user if context["mention"]: senders = list({ m["message"].sender for m in missed_messages if m["trigger"] == "mentioned" or m["trigger"] == "wildcard_mentioned" }) message = missed_messages[0]["message"] stream = Stream.objects.only("id", "name").get(id=message.recipient.type_id) stream_header = f"{stream.name} > {message.topic_name()}" context.update(stream_header=stream_header, ) else: raise AssertionError("Invalid messages!") # If message content is disabled, then flush all information we pass to email. if not message_content_allowed_in_missedmessage_emails(user_profile): realm = user_profile.realm context.update( reply_to_zulip=False, messages=[], sender_str="", realm_str=realm.name, huddle_display_name="", show_message_content=False, message_content_disabled_by_user=not user_profile. message_content_in_email_notifications, message_content_disabled_by_realm=not realm. message_content_allowed_in_email_notifications, ) else: context.update( messages=build_message_list( user=user_profile, messages=[m["message"] for m in missed_messages], stream_map={}, ), sender_str=", ".join(sender.full_name for sender in senders), realm_str=user_profile.realm.name, show_message_content=True, ) with override_language(user_profile.default_language): from_name: str = _("Zulip missed messages") from_address = FromAddress.NOREPLY if len(senders) == 1 and settings.SEND_MISSED_MESSAGE_EMAILS_AS_USER: # If this setting is enabled, you can reply to the Zulip # missed message emails directly back to the original sender. # However, one must ensure the Zulip server is in the SPF # record for the domain, or there will be spam/deliverability # problems. # # Also, this setting is not really compatible with # EMAIL_ADDRESS_VISIBILITY_ADMINS. sender = senders[0] from_name, from_address = (sender.full_name, sender.email) context.update(reply_to_zulip=False, ) email_dict = { "template_prefix": "zerver/emails/missed_message", "to_user_ids": [user_profile.id], "from_name": from_name, "from_address": from_address, "reply_to_email": str(Address(display_name=reply_to_name, addr_spec=reply_to_address)), "context": context, } queue_json_publish("email_senders", email_dict) user_profile.last_reminder = timezone_now() user_profile.save(update_fields=["last_reminder"])
def _compute_recipient(user, email): # We want to try and use the user's name, then their username, and finally # nothing to display a "Friendly" name for the recipient. return str( Address(first([user.name, user.username], default=""), addr_spec=email))
def build_email( template_prefix: str, to_user_ids: Optional[List[int]] = None, to_emails: Optional[List[str]] = None, from_name: Optional[str] = None, from_address: Optional[str] = None, reply_to_email: Optional[str] = None, language: Optional[str] = None, context: Mapping[str, Any] = {}, realm: Optional[Realm] = None, ) -> EmailMultiAlternatives: # Callers should pass exactly one of to_user_id and to_email. assert (to_user_ids is None) ^ (to_emails is None) if to_user_ids is not None: to_users = [get_user_profile_by_id(to_user_id) for to_user_id in to_user_ids] if realm is None: assert len({to_user.realm_id for to_user in to_users}) == 1 realm = to_users[0].realm to_emails = [ str(Address(display_name=to_user.full_name, addr_spec=to_user.delivery_email)) for to_user in to_users ] extra_headers = {} if realm is not None: # formaddr is meant for formatting (display_name, email_address) pair for headers like "To", # but we can use its utility for formatting the List-Id header, as it follows the same format, # except having just a domain instead of an email address. extra_headers["List-Id"] = formataddr((realm.name, realm.host)) context = { **context, "support_email": FromAddress.SUPPORT, "email_images_base_uri": settings.ROOT_DOMAIN_URI + "/static/images/emails", "physical_address": settings.PHYSICAL_ADDRESS, } def render_templates() -> Tuple[str, str, str]: email_subject = ( loader.render_to_string( template_prefix + ".subject.txt", context=context, using="Jinja2_plaintext" ) .strip() .replace("\n", "") ) message = loader.render_to_string( template_prefix + ".txt", context=context, using="Jinja2_plaintext" ) try: html_message = loader.render_to_string(template_prefix + ".html", context) except TemplateDoesNotExist: emails_dir = os.path.dirname(template_prefix) template = os.path.basename(template_prefix) compiled_template_prefix = os.path.join(emails_dir, "compiled", template) html_message = loader.render_to_string(compiled_template_prefix + ".html", context) return (html_message, message, email_subject) # The i18n story for emails is a bit complicated. For emails # going to a single user, we want to use the language that user # has configured for their Zulip account. For emails going to # multiple users or to email addresses without a known Zulip # account (E.g. invitations), we want to use the default language # configured for the Zulip organization. # # See our i18n documentation for some high-level details: # https://zulip.readthedocs.io/en/latest/translating/internationalization.html if not language and to_user_ids is not None: language = to_users[0].default_language if language: with override_language(language): # Make sure that we render the email using the target's native language (html_message, message, email_subject) = render_templates() else: (html_message, message, email_subject) = render_templates() logger.warning("Missing language for email template '%s'", template_prefix) if from_name is None: from_name = "Zulip" if from_address is None: from_address = FromAddress.NOREPLY if from_address == FromAddress.tokenized_no_reply_placeholder: from_address = FromAddress.tokenized_no_reply_address() if from_address == FromAddress.no_reply_placeholder: from_address = FromAddress.NOREPLY if from_address == FromAddress.support_placeholder: from_address = FromAddress.SUPPORT # Set the "From" that is displayed separately from the envelope-from. extra_headers["From"] = str(Address(display_name=from_name, addr_spec=from_address)) # Check ASCII encoding length. Amazon SES rejects emails with # From names longer than 320 characters (which appears to be a # misinterpretation of the RFC); in that case we drop the name # from the From line, under the theory that it's better to send # the email with a simplified From field than not. if len(sanitize_address(extra_headers["From"], "utf-8")) > 320: extra_headers["From"] = str(Address(addr_spec=from_address)) # If we have an unsubscribe link for this email, configure it for # "Unsubscribe" buttons in email clients via the List-Unsubscribe header. # # Note that Microsoft ignores URLs in List-Unsubscribe headers, as # they only support the alternative `mailto:` format, which we # have not implemented. if "unsubscribe_link" in context: extra_headers["List-Unsubscribe"] = f"<{context['unsubscribe_link']}>" extra_headers["List-Unsubscribe-Post"] = "List-Unsubscribe=One-Click" reply_to = None if reply_to_email is not None: reply_to = [reply_to_email] # Remove the from_name in the reply-to for noreply emails, so that users # see "noreply@..." rather than "Zulip" or whatever the from_name is # when they reply in their email client. elif from_address == FromAddress.NOREPLY: reply_to = [FromAddress.NOREPLY] envelope_from = FromAddress.NOREPLY mail = EmailMultiAlternatives( email_subject, message, envelope_from, to_emails, reply_to=reply_to, headers=extra_headers ) if html_message is not None: mail.attach_alternative(html_message, "text/html") return mail
def test_compile_text_undecodable_quote() -> None: from_addr = Address("Joe Q. Sender", addr_spec="*****@*****.**") to_addrs = ( Address(addr_spec="*****@*****.**"), Address("Jane Q. Recipient", addr_spec="*****@*****.**"), ) draft = DraftMessage( from_addr=from_addr, to_addrs=to_addrs, subject="This is a test e-mail.", ) draft.addtext("This is a quote:\n") draft.addblobquote( b"\xD0is is i\xF1 L\xE1tin\xB9.\n", "utf-8", "latin1.txt", ) assert email2dict(draft.compile()) == { "unixfrom": None, "headers": { "subject": "This is a test e-mail.", "from": [addr2dict(from_addr)], "to": list(map(addr2dict, to_addrs)), "user-agent": [USER_AGENT], "content-type": { "content_type": "multipart/mixed", "params": {}, }, }, "preamble": None, "content": [ { "unixfrom": None, "headers": { "content-type": { "content_type": "text/plain", "params": {}, }, }, "preamble": None, "content": "This is a quote:\n", "epilogue": None, }, { "unixfrom": None, "headers": { "content-type": { "content_type": "application/octet-stream", "params": {}, }, "content-disposition": { "disposition": "inline", "params": {"filename": "latin1.txt"}, }, }, "preamble": None, "content": b"\xD0is is i\xF1 L\xE1tin\xB9.\n", "epilogue": None, }, ], "epilogue": None, }
def emailPlainText(subject, message, recipients, should_email=True): assert isinstance(subject, str), "subject must be a str, was: {}".format(type(subject)) assert isinstance(message, str), "message must be a str, was: {}".format(type(message)) if recipients is None: logging.info("No recipients given, e-mail won't be sent: '" + subject + "'") return assert isinstance(recipients, str) or isinstance(recipients, list), ( "recipients must be a str or list, was: {}".format(type(recipients)) ) smtp = { "host": Config.get("email.smtp.host", None), "port": Config.get("email.smtp.port", None), "user": Config.get("email.smtp.user", None), "pass": Config.get("email.smtp.pass", None) } sender = Address(Config.get("email.sender.name", "undefined"), addr_spec=Config.get("email.sender.address", "*****@*****.**")) if isinstance(recipients, str): recipients = [recipients] if not should_email: logging.info("[e-mail] Not sending plain text email") else: if Config.get("test"): subject = "[test] " + subject filtered_recipients = [] for recipient in recipients: if recipient in Config.get("email.allowed_email_addresses_in_test"): filtered_recipients.append(recipient) recipients = filtered_recipients # 1. build e-mail msg = EmailMessage() msg['Subject'] = subject msg['From'] = sender msg['To'] = Report.emailStringsToAddresses(recipients) msg.set_content(message) # 2. send e-mail if not msg["To"]: logging.warning("[e-mail] Email with subject \"{}\" has no recipients".format(subject)) else: logging.info("[e-mail] Sending email with subject \"{}\" to: {}".format(subject, ", ".join(recipients))) if isinstance(smtp["host"], str) and isinstance(smtp["port"], str): with smtplib.SMTP(smtp["host"] + ":" + smtp["port"]) as s: s.ehlo() # s.starttls() if smtp["user"] and smtp["pass"]: s.login(smtp["user"], smtp["pass"]) else: logging.debug("[e-mail] user/pass not configured") logging.debug("[e-mail] sending…") s.send_message(msg) logging.debug("[e-mail] sending complete.") else: logging.warning("[e-mail] host/port not configured") Slack.slack(text=subject, attachments=None)
from django.core.mail import send_mail from django.core.signing import TimestampSigner from django.db import transaction from django.template import TemplateDoesNotExist from django.template.loader import get_template from django.test import override_settings, RequestFactory from email.headerregistry import Address import logging logger = logging.getLogger(__name__) organizers = Address("Outreachy Organizers", "organizers", "outreachy.org") applicant_help = Address("Outreachy Applicant Helpers", "applicant-help", "outreachy.org") mentors_mailing_list = Address("Outreachy mentors list", "mentors", "lists.outreachy.org") def send_template_mail(template_name, context, recipient_list, request=None, **kwargs): # Only load the template once, no matter how many messages we're sending. template = get_template(template_name, using='plaintext') for recipient in recipient_list: # Templates used with this function expect the 'recipient' context # variable to contain a single address, not a list, so override # send_group_template_mail's default. context['recipient'] = recipient send_group_template_mail(template, context, [recipient], request,
from email.headerregistry import Address from email.message import EmailMessage import os import smtplib import sys # Gmail details email_address = "*****@*****.**" email_password = "******" # Recipent to_address = ( Address(username='******', domain='hotmail.com'), Address(username='******', domain='lohjanpallo.fi') ) def create_email_message(from_address, to_address, subject, body): msg = EmailMessage() msg['From'] = from_address msg['To'] = to_address msg['Subject'] = subject msg.set_content(body) return msg if sys.argv[1] == '0': title='Sähkökatko on ohi' text='Sähkökatko ohi kuplahallissa!' else: title='Sähkökatko' text='Sähkökatko kuplahallissa!'
def test_find_email_addresses_single_header_finds_one_address(self): text = """From: John Doe <*****@*****.**>""" addrs = find_email_addresses(text, ["from"]) expected = [Address(display_name="John Doe", username="******", domain="machine.example")] self.assertEqual(expected, addrs)
from email.headerregistry import Address, Group from typing import List, Union import pytest from mailbits import parse_addresses @pytest.mark.parametrize( "s,addresses", [ ("", []), ( "Some User <*****@*****.**>", [Address("Some User", addr_spec="*****@*****.**")], ), ( "Some User <*****@*****.**>, [email protected]", [ Address("Some User", addr_spec="*****@*****.**"), Address(addr_spec="*****@*****.**"), ], ), ( ( "friends: [email protected], [email protected];," " enemies:[email protected], [email protected];" ), [ Group( "friends", ( Address(addr_spec="*****@*****.**"),