]> git.ipfire.org Git - ipfire.org.git/commitdiff
Implement drip campaigns
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 19 Nov 2019 13:46:07 +0000 (13:46 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 19 Nov 2019 13:47:20 +0000 (13:47 +0000)
When users sign up, they will now receive a number of emails to
help them setting up their accounts and getting involved with
IPFire.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/backend/accounts.py
src/backend/base.py
src/backend/campaigns.py [new file with mode: 0644]
src/backend/messages.py
src/crontab/ipfire
src/templates/auth/messages/donation-reminder.txt [new file with mode: 0644]
src/templates/auth/messages/profile-setup-2.txt [new file with mode: 0644]
src/templates/auth/messages/profile-setup.txt [new file with mode: 0644]
src/templates/auth/messages/register.txt

index a098280def2a9b0cc475f8c093ba81970d3d2796..29b8a516f283e329863926b137434150d7f44663 100644 (file)
@@ -51,6 +51,7 @@ backend_PYTHON = \
        src/backend/accounts.py \
        src/backend/base.py \
        src/backend/blog.py \
+       src/backend/campaigns.py \
        src/backend/countries.py \
        src/backend/database.py \
        src/backend/decorators.py \
@@ -123,7 +124,10 @@ templates_auth_DATA = \
 templates_authdir = $(templatesdir)/auth
 
 templates_auth_messages_DATA = \
+       src/templates/auth/messages/donation-reminder.txt \
        src/templates/auth/messages/password-reset.txt \
+       src/templates/auth/messages/profile-setup.txt \
+       src/templates/auth/messages/profile-setup-2.txt \
        src/templates/auth/messages/register.txt
 
 templates_auth_messagesdir = $(templates_authdir)/messages
index 46a5061bf3779f81709ce57baa88529ff8640b31..6d01ac2e072a48779db98d089e92ebf548ee8f5e 100644 (file)
@@ -374,6 +374,9 @@ class Accounts(Object):
                self.backend.messages.send_template("people/messages/new-account",
                        recipients=["moderators@ipfire.org"], account=account)
 
+               # Launch all drip campaigns
+               self.backend.campaigns.launch(account)
+
                return account
 
        def create(self, uid, email, first_name, last_name, country_code=None):
@@ -799,6 +802,10 @@ class Account(LDAPObject):
        def email(self):
                return self._get_string("mail")
 
+       @property
+       def email_to(self):
+               return "%s <%s>" % (self, self.email)
+
        # Mail Routing Address
 
        def get_mail_routing_address(self):
index f85d2ad891c88c112e57e95235dceabeb2c7cb3b..80c509980d0012c7443c3fa4700161dacb02a13a 100644 (file)
@@ -6,6 +6,7 @@ import tornado.httpclient
 
 from . import accounts
 from . import blog
+from . import campaigns
 from . import database
 from . import geoip
 from . import fireinfo
@@ -98,6 +99,8 @@ class Backend(object):
                        "check-mirrors"     : self.mirrors.check_all,
                        "check-spam"        : self.accounts.check_spam,
                        "cleanup"           : self.cleanup,
+                       "launch-campaigns"  : self.campaigns.launch_manually,
+                       "run-campaigns"     : self.campaigns.run,
                        "scan-files"        : self.releases.scan_files,
                        "send-all-messages" : self.messages.queue.send_all,
                        "test-blacklist"    : self.geoip.test_blacklist,
@@ -119,6 +122,10 @@ class Backend(object):
                if r:
                        raise SystemExit(r)
 
+       @lazy_property
+       def campaigns(self):
+               return campaigns.Campaigns(self)
+
        @lazy_property
        def groups(self):
                return accounts.Groups(self)
diff --git a/src/backend/campaigns.py b/src/backend/campaigns.py
new file mode 100644 (file)
index 0000000..746870e
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/python3
+
+import logging
+
+from .decorators import *
+from .misc import Object
+
+class Campaigns(Object):
+       async def launch_manually(self, uid):
+               account = self.backend.accounts.get_by_uid(uid)
+               if account:
+                       self.launch(account)
+
+       def launch(self, account):
+               logging.debug("Launching all campaigns for %s" % account)
+
+               # Update old timestamps first
+               self.db.execute("UPDATE campaign_templates \
+                       SET launch_at = launch_at + repeat_after \
+                       WHERE (launch_at IS NOT NULL AND launch_at <= CURRENT_TIMESTAMP) \
+                               AND repeat_after IS NOT NULL")
+
+               # Launch all campaigns
+               self.db.execute("INSERT INTO campaign_emails(account_uid, template, \
+                       launch_at, repeat_after, groups ) \
+                       SELECT %s, template, COALESCE(launch_at, CURRENT_TIMESTAMP + launch_after), \
+                               repeat_after, groups FROM campaign_templates", account.uid)
+
+       def _get_campaign_emails(self, query, *args):
+               res = self.db.query(query, *args)
+
+               for row in res:
+                       yield CampaignEmail(self.backend, row.id, data=row)
+
+       async def run(self):
+               with self.db.transaction():
+                       emails = self._get_campaign_emails("SELECT * FROM campaign_emails \
+                               WHERE launch_at <= CURRENT_TIMESTAMP ORDER BY launch_at")
+
+                       # Send them all
+                       for email in emails:
+                               email.send()
+
+
+class CampaignEmail(Object):
+       def init(self, id, data=None):
+               self.id   = id
+               self.data = data
+
+       @lazy_property
+       def account(self):
+               return self.backend.accounts.get_by_uid(self.data.account_uid)
+
+       @property
+       def template(self):
+               return self.data.template
+
+       @property
+       def repeat_after(self):
+               return self.data.repeat_after
+
+       def send(self):
+               # Delete if the account does not exist any more
+               if not self.account:
+                       return self._delete()
+
+               logging.info("Sending %s to %s" % (self.template, self.account))
+
+               # Generate the email
+               self.backend.messages.send_template(self.template, account=self.account)
+
+               # Update this email for the next launch
+               self._update()
+
+       def _update(self):
+               # If this email repeats, we will update the timestamp
+               if self.repeat_after:
+                       self.db.execute("UPDATE campaign_emails \
+                               SET launch_at = launch_at + repeat_after \
+                               WHERE id = %s", self.id)
+
+               # Otherwise we will delete it
+               else:
+                       self._delete()
+
+       def _delete(self):
+               """
+                       Deletes this email
+               """
+               self.db.execute("DELETE FROM campaign_emails WHERE id = %s", self.id)
index 1b0e85a2bd47ecb6e51b9c1a77e8b1e0a2547620..28b717c07f2be70f9b40ee6196a7ee3906071061 100644 (file)
@@ -6,6 +6,7 @@ import email.mime.nonmultipart
 import email.utils
 import logging
 import subprocess
+import tornado.locale
 import tornado.template
 
 from . import accounts
index eb3e52b9a327eeba627c645ade0f0cdfab44ea39..df1293c63f6eee867bd91e3bb48541fd1cf92cd3 100644 (file)
@@ -9,6 +9,9 @@ SHELL=/bin/bash
 # Send messages
 * * * * *      nobody  ipfire.org send-all-messages
 
+# Run campaigns
+*/5 * * * *    nobody  ipfire.org run-campaigns
+
 # Cleanup once an hour
 30 * * * *     nobody  ipfire.org cleanup
 
diff --git a/src/templates/auth/messages/donation-reminder.txt b/src/templates/auth/messages/donation-reminder.txt
new file mode 100644 (file)
index 0000000..2f06626
--- /dev/null
@@ -0,0 +1,28 @@
+From: Michael Tremer from the IPFire Project <michael.tremer@ipfire.org>
+To: {{ account.email_to }}
+Subject: {{ _("Please help us with your donation!") }}
+
+{{ _("Hey again, %s,") % account.first_name }}
+
+{{ _("IPFire runs on supporters' donations, people like you!") }}
+
+{{ _("Why do we need you donations?") }}
+
+* {{ _("Your money ensures the longevity and long-term success of this project.") }}
+* {{ _("It helps us fund developers and extend our skills") }}
+* {{ _("It will aid us to promote IPFire to more people around the world") }}
+* {{ _("This funds conferences, where we focus on future projects") }}
+* {{ _("It pays for our hosting") }}
+
+{{ _("All this, as you would understand, requires money. Every single donation counts.") }}
+
+{{ _("If you want to see IPFire thrive, we need your support.") }}
+
+{{ _("The best way to do this is by setting up a monthly donation which you can do here:") }}
+
+  https://www.ipfire.org/donate?frequency=monthly&amount=10
+
+{{ _("We also have other ways to donate. Please go to https://www.ipfire.org/donate for details.") }}
+
+{{ _("Thank you so much for your support,") }}
+{{ _("-Michael")}}
diff --git a/src/templates/auth/messages/profile-setup-2.txt b/src/templates/auth/messages/profile-setup-2.txt
new file mode 100644 (file)
index 0000000..0a9c4ae
--- /dev/null
@@ -0,0 +1,17 @@
+From: Arne Fitzenreiter from the IPFire Project <arne.fitzenreiter@ipfire.org>
+To: {{ account.email_to }}
+Subject: {{ _("Hi!") }}
+
+{{ _("Hello once again, %s,") % account.first_name }}
+
+{{ _("we hope you are enjoying using IPFire.") }}
+
+{{ _("Did you know that you can get help from our community at https://community.ipfire.org?") }}
+{{ _("People like me often post on here, providing help and support.") }}
+
+{{ _("But we also rely on you donations. Please consider helping us by setting up a small monthly donation:") }}
+
+  https://www.ipfire.org/donate?frequency=monthly
+
+{{ _("Thank you, we really appreciate your support,") }}
+{{ _("-Arne")}}
diff --git a/src/templates/auth/messages/profile-setup.txt b/src/templates/auth/messages/profile-setup.txt
new file mode 100644 (file)
index 0000000..5796338
--- /dev/null
@@ -0,0 +1,19 @@
+From: Michael Tremer from the IPFire Project <michael.tremer@ipfire.org>
+To: {{ account.email_to }}
+Subject: {{ _("Welcome to the IPFire Project!") }}
+
+{{ _("Hello %s!") % account.first_name }}
+
+{{ _("I would like to introduce myself: I'm Michael and I am one of the people behind the project. We are a team of passionate people who try to make the Internet a better place. On behalf of everyone, I would like to say: Welcome to the IPFire Project!") }}
+
+{{ _("We want you to feel a part of our team. Can I ask you to set up your profile? We would love to know a little bit more about you.") }}
+{{ _("To do this, please log on to your profile and click the edit button.") }}
+
+{{ _("I would also like to invite you to join our community at https://community.ipfire.org, if you haven't already done so.") }}
+
+{{ _("Finally, this organisation relies on the generous donations of people like you. If you can, please consider supporting this project and the team behind it, so that we can continue our long-term vision, fund developers and promote our project.") }}
+
+{{ _("You can do this at https://www.ipfire.org/donate.")  }}
+
+{{ _("Thank you,") }}
+{{ _("-Michael") }}
index e1adf20ca231c80088bec0aaeacaec40a731ae93..4d2a02d0c188302d1815b5698ed2b8cd96b251bb 100644 (file)
@@ -1,6 +1,6 @@
 From: IPFire Project <no-reply@ipfire.org>
 To: {{ first_name }} {{ last_name }} <{{ email }}>
-Subject: {{ _("Welcome to the IPFire Project!") }}
+Subject: {{ _("Please activate your account for the IPFire Project") }}
 
 {{ _("Hello %s!") % first_name }}