mirror of
https://github.com/postgres/pgweb.git
synced 2025-07-25 16:02:27 +00:00
Add support for staggering outgoing emails
Sent email can be assigned a "stagger type", for which he system will maintain a "last sent" information. When the email is sent, it will be delayed to be at least "stagger" time after the last one sent of the same type. If no email of this type has been sent before, the email is of course sent immediately.
This commit is contained in:
@ -9,6 +9,7 @@ from .models import QueuedMail
|
||||
class QueuedMailAdmin(admin.ModelAdmin):
|
||||
model = QueuedMail
|
||||
readonly_fields = ('parsed_content', )
|
||||
list_display = ('pk', 'sender', 'receiver', 'sendat')
|
||||
|
||||
def parsed_content(self, obj):
|
||||
# We only try to parse the *first* piece, because we assume
|
||||
|
@ -9,6 +9,7 @@ from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import connection
|
||||
from django.conf import settings
|
||||
|
||||
import datetime
|
||||
import smtplib
|
||||
|
||||
from pgweb.mailqueue.models import QueuedMail
|
||||
@ -26,7 +27,7 @@ class Command(BaseCommand):
|
||||
if not curs.fetchall()[0][0]:
|
||||
raise CommandError("Failed to get advisory lock, existing send_queued_mail process stuck?")
|
||||
|
||||
for m in QueuedMail.objects.all():
|
||||
for m in QueuedMail.objects.filter(sendat__lte=datetime.datetime.now()):
|
||||
# Yes, we do a new connection for each run. Just because we can.
|
||||
# If it fails we'll throw an exception and just come back on the
|
||||
# next cron job. And local delivery should never fail...
|
||||
|
28
pgweb/mailqueue/migrations/0002_stagger_sending.py
Normal file
28
pgweb/mailqueue/migrations/0002_stagger_sending.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Generated by Django 2.2.11 on 2020-10-29 14:16
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mailqueue', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LastSent',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type', models.CharField(max_length=10, unique=True)),
|
||||
('lastsent', models.DateTimeField()),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='queuedmail',
|
||||
name='sendat',
|
||||
field=models.DateTimeField(default=datetime.datetime(2000, 1, 1, 0, 0)),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -10,6 +10,12 @@ class QueuedMail(models.Model):
|
||||
# Flag if the message is "user generated", so we can treat those
|
||||
# separately from an antispam and delivery perspective.
|
||||
usergenerated = models.BooleanField(null=False, blank=False, default=False)
|
||||
sendat = models.DateTimeField(null=False, blank=False)
|
||||
|
||||
def __str__(self):
|
||||
return "%s: %s -> %s" % (self.pk, self.sender, self.receiver)
|
||||
|
||||
|
||||
class LastSent(models.Model):
|
||||
type = models.CharField(max_length=10, null=False, blank=False, unique=True)
|
||||
lastsent = models.DateTimeField(null=False, blank=False)
|
||||
|
@ -1,3 +1,6 @@
|
||||
from django.db import transaction
|
||||
|
||||
from datetime import datetime
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.nonmultipart import MIMENonMultipart
|
||||
@ -6,7 +9,7 @@ from email.utils import make_msgid
|
||||
from email import encoders, charset
|
||||
from email.header import Header
|
||||
|
||||
from .models import QueuedMail
|
||||
from .models import QueuedMail, LastSent
|
||||
|
||||
|
||||
def _encoded_email_header(name, email):
|
||||
@ -22,7 +25,7 @@ _utf8_charset.header_encoding = charset.QP
|
||||
_utf8_charset.body_encoding = charset.QP
|
||||
|
||||
|
||||
def send_simple_mail(sender, receiver, subject, msgtxt, attachments=None, usergenerated=False, cc=None, replyto=None, sendername=None, receivername=None, messageid=None, suppress_auto_replies=True, is_auto_reply=False, htmlbody=None, headers={}):
|
||||
def send_simple_mail(sender, receiver, subject, msgtxt, attachments=None, usergenerated=False, cc=None, replyto=None, sendername=None, receivername=None, messageid=None, suppress_auto_replies=True, is_auto_reply=False, htmlbody=None, headers={}, staggertype=None, stagger=None):
|
||||
# attachment format, each is a tuple of (name, mimetype,contents)
|
||||
# content should be *binary* and not base64 encoded, since we need to
|
||||
# use the base64 routines from the email library to get a properly
|
||||
@ -79,10 +82,20 @@ def send_simple_mail(sender, receiver, subject, msgtxt, attachments=None, userge
|
||||
encoders.encode_base64(part)
|
||||
msg.attach(part)
|
||||
|
||||
# Just write it to the queue, so it will be transactionally rolled back
|
||||
QueuedMail(sender=sender, receiver=receiver, fullmsg=msg.as_string(), usergenerated=usergenerated).save()
|
||||
if cc:
|
||||
# Write a second copy for the cc, wihch will be delivered
|
||||
# directly to the recipient. (The sender doesn't parse the
|
||||
# message content to extract cc fields).
|
||||
QueuedMail(sender=sender, receiver=cc, fullmsg=msg.as_string(), usergenerated=usergenerated).save()
|
||||
with transaction.atomic():
|
||||
if staggertype and stagger:
|
||||
# Don't send a second one too close after another one of this class.
|
||||
ls, created = LastSent.objects.get_or_create(type=staggertype, defaults={'lastsent': datetime.now()})
|
||||
|
||||
sendat = ls.lastsent = ls.lastsent + stagger
|
||||
ls.save(update_fields=['lastsent'])
|
||||
else:
|
||||
sendat = datetime.now()
|
||||
|
||||
# Just write it to the queue, so it will be transactionally rolled back
|
||||
QueuedMail(sender=sender, receiver=receiver, fullmsg=msg.as_string(), usergenerated=usergenerated, sendat=sendat).save()
|
||||
if cc:
|
||||
# Write a second copy for the cc, wihch will be delivered
|
||||
# directly to the recipient. (The sender doesn't parse the
|
||||
# message content to extract cc fields).
|
||||
QueuedMail(sender=sender, receiver=cc, fullmsg=msg.as_string(), usergenerated=usergenerated, sendat=sendat).save()
|
||||
|
Reference in New Issue
Block a user