Отправка текстового + HTML-сообщения электронной почты с вложением календаря ICS в Django или Python

avatar
Ciske
1 июля 2021 в 19:38
1881
4
5

Я искал библиотеку или, по крайней мере, функциональный фрагмент кода, который позволяет мне отправлять электронные письма из Django (или, по крайней мере, на Python) с текстовым содержимым, содержимым HTML и вложением календаря ICS, которое распознается каждым основным почтовым клиентом. Для моего конкретного случая использования достаточно, если пользователю предлагается кнопка «Добавить в календарь».

Я чувствую, что эта проблема уже должна быть решена, но я нахожу только ответы, которые относятся к библиотекам, которые не обслуживаются, устарели или неполны каким-либо другим образом. Я протестировал пару фрагментов, которые будут прикреплять файл ICS, но G-mail не дает мне возможности добавить его в календарь, как обычно.

Есть ли готовое решение, которое мне не хватает?

Источник
Brian Destura
12 июля 2021 в 00:52
1

Вы смотрели на: coderhelper.com/questions/4823574/…

Ciske
13 июля 2021 в 12:46
0

У меня есть @bdbd, он устарел, ему 8 лет (пакеты изменились с тех пор, как он был написан) и переписывая его с новой упаковкой, он вылетает, когда я пытаюсь это сделать, на msg.as_string(). Он также неполный: например, он создает ical_atch, но никогда не используется. Я имею в виду ответ, получивший наибольшее количество голосов от Оберона Вашера.

Kovy Jacob
14 июля 2021 в 00:28
0

Что такое вложение календаря ics? Не понимаю, почему вы не можете сделать первые 2 с собственным модулем электронной почты django.

Ответы (4)

avatar
Ciske
1 сентября 2021 в 15:01
0

Таким образом, ключ был в том, чтобы прикрепить файл ICS как файл, а не как строку (используя django.core.mail.message.EmailMessage.attach_alternative()).

Следующий фрагмент работает для меня в почте Gmail, Hotmail и Yahoo (MS Outlook необходимо подтвердить), то есть информация о событии календаря отображается вместе с электронным письмом, и, по крайней мере, Gmail и Hotmail предоставляют возможность добавить событие в свой календарь.

from django.core.mail.message import EmailMultiAlternatives  # At the top of your .py file

email = EmailMultiAlternatives(subject, message, settings.FROM_EMAIL, ['recipient@email.here'])
# email.attach_alternative('<b>html here</b>', 'text/html') # Optional HTML message
email.attach_file(filename_event, 'text/calendar')
email.send(fail_silently=False)

Я использую ics https://pypi.org/project/ics/ для создания файла ICS. Этот пакет в настоящее время все еще поддерживается. Единственная другая крупная библиотека файлов Python ics, которую я смог найти, это ical https://pypi.org/project/icalendar/, и источник для этого не обновлялся в течение года с 1 сентября. , 2021.

Этот код работает для меня, чтобы создать файл ics:

from ics import Calendar, Event  # At the top of your .py file

ICS_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"

calendar = Calendar()
event = Event()
event.name = _("Our event name")
event.begin = appointment.start_time.strftime(ICS_DATETIME_FORMAT)
event.end = appointment.end_time.strftime(ICS_DATETIME_FORMAT)
event.organizer = settings.DEFAULT_FROM_EMAIL
calendar.events.add(event)
filename_event = 'invite-%d.ics' % appointment.id
with open(filename_event, 'w') as ics_file:
    ics_file.writelines(calendar)

где назначение — это мой собственный класс Django, где start_time и end_time имеют тип DateTimeField.

Если вы создаете новый файл ics для каждого запроса, важно также иметь уникальное имя файла для каждого запроса, чтобы исключить риск одновременной записи в файл двух отдельных запросов.

После отправки файла ICS я удалю его следующим образом:

import os  # At the top of your .py file

os.remove(filename_event)
avatar
Jay Jain
17 июля 2021 в 07:47
-1

Вы можете попробовать использовать mark_safe(), он отображает код js,html в виде строки на html-странице. Я использовал его для настройки администратора Django. Посмотрите пример ниже:

some_sample_string = '''<h1>This is some sample </h1>'''
my_sample_html = mark_safe(some_sample_string)

вы можете спроектировать страницу в HTML, добавить к ней индивидуальный дизайн, а затем вернуть байтовый объект mark_safe в HTML или любую веб-страницу и пометить его там, это будет работать.

вы можете проверить эти ссылки, они могут вам помочь

https://www.kite.com/python/docs/django.utils.safestring.mark_safe

Вернуть строку mark_safe из __str__

https://www.fullstackpython.com/django-utils-safestring-mark-safe-examples.html

https://docs.djangoproject.com/en/3.0/_modules/django/utils/html/

Ciske
21 октября 2021 в 13:16
0

Вы предназначали этот ответ для другого вопроса? Я не понимаю, какое отношение это имеет к моему вопросу ;-p

avatar
Rutger
16 июля 2021 в 22:22
5

Начните с создания файла .ics. Конечно, это также можно сделать с помощью скрипта Python для создания динамических файлов .ics.

Файл .ics

Дополнительная информация об iCalendar: основная информация и общие шаблоны (см. пример ниже).

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
BEGIN:VEVENT
UID:uid1@example.com
DTSTAMP:19970714T170000Z
ORGANIZER;CN=John Doe:MAILTO:john.doe@example.com
DTSTART:19970714T170000Z
DTEND:19970715T035959Z
SUMMARY:Bastille Day Party
GEO:48.85299;2.36885
END:VEVENT
END:VCALENDAR

Вы также можете создать свой собственный iCalendar, если вам так проще (см. пример ниже).

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//ical.marudot.com//iCal Event Maker
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/Berlin
LAST-MODIFIED:20201011T015911Z
TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20210716T221958Z
UID:20210716T221958Z-901688629@marudot.com
DTSTART;TZID=Europe/Berlin:20210717T120000
DTEND;TZID=Europe/Berlin:20210717T160000
SUMMARY:Stack Overflow
DESCRIPTION:iCalendar example for Stack Overflow user Ciske\n
LOCATION:Amsterdam
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Stack Overflow
TRIGGER:-PT1H
END:VALARM
END:VEVENT
END:VCALENDAR

Django settings.py

EMAIL_HOST = SMTP server.
EMAIL_HOST_USER = Login credentials for the SMTP server.
EMAIL_HOST_PASSWORD = Password credential for the SMTP server.
EMAIL_PORT = SMTP server port.
EMAIL_USE_TLS or _SSL = True if secure connection.

Django views.py

Отправить электронное письмо с файлом .ics в качестве вложения.

from django.core.mail import EmailMessage

# Send email with attachment
email = EmailMessage(
    'Subject',
    'Email body',
    'from@example.com',
    ['to@example.com']
)
email.attach_file('assets/invite.ics', 'text/calendar')
email.send()

Вы даже можете добавить файл .html в свою электронную почту.

from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string

html_content = render_to_string('assets/email.html', context)

email = EmailMultiAlternatives('Subject', 'Email body', 'from@example.com', [to@example.com])
email.attach_alternative(html_content, 'text/html')
email.attach_file('assets/invite.ics', 'text/calendar')
email.send()
Ciske
17 июля 2021 в 19:17
1

Я был уверен, что это не сработает, потому что я уже пробовал это, но попробовал еще раз, с вашим кодом, он работает, по крайней мере, в Gmail, вложение календаря отображается правильно со всей информацией о встрече в сверху с возможностью «добавить в календарь» одним щелчком мыши. Я еще не тестировал это для других почтовых клиентов, но, поскольку период вознаграждения закончился, и это был самый полезный ответ на то, что я хотел сделать, вознаграждение принадлежит вам. РЕДАКТИРОВАТЬ разница между вашим фрагментом и моим заключается в том, что я использовал для приглашения attach_alternative() вместо attach_file().

Rutger
17 июля 2021 в 19:26
0

Спасибо за награду! @Ciske Я рад слышать, что это сработало в Gmail. Дайте мне знать, если это работает в разных почтовых клиентах. Если нет, мы можем предложить другое решение.

avatar
Anton
14 июля 2021 в 15:49
0

Django имеет встроенное решение, основанное на smtplib Python:

https://docs.djangoproject.com/en/3.2/topics/email/

Вам необходимо предоставить некоторые учетные данные для вашего smtp-сервера (gmail, mailgun и т. д.) в настройках, после чего вы сможете использовать модуль django.core.mail.

Для прикрепления материалов вы можете использовать EmailMessage.attach() или attach_file()

https://docs.djangoproject.com/en/3.2/topics/email/#django.core.mail.EmailMessage

Anton
14 июля 2021 в 15:50
0

«Функции Django send_mail() и send_mass_mail() на самом деле являются тонкими оболочками, которые используют класс EmailMessage». Итак, для прикрепления вам 100% нужен класс EmailMessage

Ciske
14 июля 2021 в 20:58
0

Из вопроса: """Я протестировал несколько фрагментов, которые будут прикреплять файл ICS, но G-mail не дает мне возможности добавить его в календарь, как это обычно делается.""" Возможно, мне следует подчеркнул, что больше. Это ключевой момент. Я могу прикрепить файл ics, но это не значит, что он будет корректно отображаться в Gmail или Outlook. Кажется, очень важно, как именно вы создаете сообщение электронной почты. Например, если вы используете многопартийный/альтернативный или многочастный/смешанный и если вы добавляете правильные заголовки в часть календаря. Поэтому в идеале я хотел бы использовать библиотеку, которая точно знает, как это сделать.

Anton
15 июля 2021 в 10:04
0

Каждый "крупный почтовый клиент" имеет собственный формат для связывания файлов ics. Вы можете проверить здесь -> labnol.org/apps/calendar.html 'Ссылки календаря' Этот парень генерирует ссылки отдельно для каждого почтового клиента. Так что 90% того «готового решения», которое вы хотите найти, не существует. Возможно, у некоторых клиентов есть свои собственные библиотеки для работы со своими сервисами (например, google-> developers.google.com/calendar/api/quickstart/python). Но я почти уверен, что решение для ВСЕХ основных клиентов не существуют

Ciske
15 июля 2021 в 20:25
0

Спасибо @Anton за эту ссылку о создании ссылок :) Я сохраню это как резервный вариант.