Skip to content

Mail

mail

Mail

Mail template

Use the data field to store additional information

You can use the typical Format String Syntax and the objects recipient and mail to access metadata to complement the template, e.g.:

Hello {recipient.address_as},

We hope it's ok to address you your first name rather than using your full name being {recipient.name}.
Have you read the email's subject '{mail.subject}'? How is your work right now at {recipient.data.company}?

Cheers!

body: str instance-attribute

data: MetaData | None = None class-attribute instance-attribute

recipients: list[Recipient] instance-attribute

subject: str instance-attribute

MailClient(config: Config | None = None)

Mail client for mass mails via Mailgun

Source code in src/pytanis/mailgun/mail.py
def __init__(self, config: Config | None = None):
    if config is None:
        config = get_cfg()
    self._config = config

batch_size: int = 10 class-attribute instance-attribute

timeout: int = 10 class-attribute instance-attribute

wait_time: int = 20 class-attribute instance-attribute

send(mail: Mail)

Send a mail to all recipients using Mailgun

Source code in src/pytanis/mailgun/mail.py
def send(self, mail: Mail):
    """Send a mail to all recipients using Mailgun"""
    errors = []
    responses = []

    # TODO: improve Mailgun batch mailing by setting custom transactional variables
    if self._config.Mailgun is None:
        msg = 'Mailgun configuration is missing'
        raise RuntimeError(msg)
    for idx, recipient in enumerate(tqdm(mail.recipients), start=1):
        try:
            recipient_mail = mail.model_copy()
            if self._config.Mailgun.token is None:
                msg = 'API token for Mailgun is empty'
                raise RuntimeError(msg)
            if self._config.Mailgun.from_address is None:
                msg = 'From Email for Mailgun is empty'
                raise RuntimeError(msg)
            if self._config.Mailgun.reply_to is None:
                msg = 'Reply To Email for Mailgun is empty'
                raise RuntimeError(msg)

            response = requests.post(
                'https://api.eu.mailgun.net/v3/mg.pycon.de/messages',
                auth=('api', self._config.Mailgun.token),
                data={
                    'to': [recipient.email],
                    'from': self._config.Mailgun.from_address,
                    'subject': recipient_mail.subject.format(recipient=recipient, mail=mail),
                    'text': recipient_mail.body.format(recipient=recipient, mail=mail),
                    'h:Reply-To': self._config.Mailgun.reply_to,
                },
                timeout=self.timeout,
            )
            # check response status message and throw exception if not 200
            response.raise_for_status()
        except Exception as e:
            errors.append((recipient, e))
        else:
            responses.append(response)

        if idx % self.batch_size == 0:
            time.sleep(self.wait_time)

    return responses, errors

MetaData

Additional, arbitrary metadata provided by the user like for template filling

model_config = ConfigDict(extra='allow') class-attribute instance-attribute

Recipient

Details about the recipient

Use the data field to store additional information

address_as: str | None = None class-attribute instance-attribute

data: MetaData | None = None class-attribute instance-attribute

email: str instance-attribute

name: str instance-attribute

fill_with_name(v, values) classmethod

Source code in src/pytanis/mailgun/mail.py
@validator('address_as')
@classmethod
def fill_with_name(cls, v, values):
    if v is None:
        v = values['name']
    return v