diff --git a/apps/newsletters/models.py b/apps/newsletters/models.py index 71a836239..1ad8930cf 100644 --- a/apps/newsletters/models.py +++ b/apps/newsletters/models.py @@ -1,3 +1,82 @@ +import datetime +import re from django.db import models +from django.contrib.auth.models import User +from django.core.urlresolvers import reverse +from apps.rss_feeds.models import Feed, MStory, MFetchHistory +from apps.reader.models import UserSubscription, UserSubscriptionFolders +from apps.profile.models import Profile +from utils import log as logging -# Create your models here. +class EmailNewsletter: + + def receive_newsletter(self, params): + user = self.user_from_email(params['recipient']) + if not user: + return + + sender_name, sender_domain = params['sender'].split('@') + feed_address = self.feed_address(user, params['sender']) + + usf = UserSubscriptionFolders.objects.get(user=user) + usf.add_folder('', 'Newsletters') + + try: + feed = Feed.objects.get(feed_address=feed_address) + except Feed.DoesNotExist: + feed = Feed.objects.create(feed_address=feed_address, + feed_link=sender_domain, + feed_title=sender_name, + fetched_once=True, + known_good=True) + feed.set_next_scheduled_update() + + try: + usersub = UserSubscription.objects.get(user=user, feed=feed) + except UserSubscription.DoesNotExist: + _, _, usersub = UserSubscription.add_subscription( + user=user, + feed_address=feed_address, + folder='Newsletters' + ) + + story_hash = MStory.ensure_story_hash(params['signature'], feed.pk) + story_params = { + "story_feed_id": feed.pk, + "story_date": datetime.datetime.fromtimestamp(int(params['timestamp'])), + "story_title": params['subject'], + "story_content": params['body-html'], + "story_author_name": sender_name, + "story_permalink": reverse('newsletter-story', + kwargs={'story_hash': story_hash}), + "story_guid": params['signature'], + } + try: + story = MStory.objects.get(story_hash=story_hash) + except MStory.DoesNotExist: + story = MStory(**story_params) + story.save() + + MFetchHistory.add(feed_id=feed.pk, fetch_type='push') + + return story + + def user_from_email(self, email): + tokens = re.search('(\w+)\+(\w+)@newsletters.newsblur.com', email) + if not tokens: + return + + username, secret_token = tokens.groups() + try: + profiles = Profile.objects.filter(secret_token=secret_token) + if not profiles: + return + profile = profiles[0] + except Profile.DoesNotExist: + return + + return profile.user + + def feed_address(self, user, sender): + return 'newsletter:%s:%s' % (user.pk, sender) + \ No newline at end of file diff --git a/apps/newsletters/urls.py b/apps/newsletters/urls.py index 2fc142784..2d7759a2f 100644 --- a/apps/newsletters/urls.py +++ b/apps/newsletters/urls.py @@ -2,5 +2,6 @@ from django.conf.urls import * from apps.newsletters import views urlpatterns = patterns('', - url(r'^/?$', views.newsletter_receive, name='newsletter-receive'), + url(r'^receive/?$', views.newsletter_receive, name='newsletter-receive'), + url(r'^story/(?P[\w:]+)/?$', views.newsletter_story, name='newsletter-story'), ) diff --git a/apps/newsletters/views.py b/apps/newsletters/views.py index 39056b929..bc10f9121 100644 --- a/apps/newsletters/views.py +++ b/apps/newsletters/views.py @@ -1,8 +1,50 @@ from django.http import HttpResponse, Http404 from utils import log as logging - +from apps.newsletters.models import EmailNewsletter +from apps.rss_feeds.models import Feed, MStory def newsletter_receive(request): - logging.debug(request.REQUEST) + params = { + 'stripped-signature':'Thanks,\nBob', + 'From':'Bob ', + 'attachment-count':'2', + 'To':'Alice ', + 'subject':'Re: Sample POST request', + 'from':'Bob ', + 'User-Agent':'Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130308 Thunderbird/17.0.4', + 'stripped-html':'\n
\n
Hi Alice,
\n

\n
This is Bob. 
\n

\n I also attached a file.

\n
Thanks,
\n
Bob
\n

\n
', + 'In-Reply-To':'<517AC78B.5060404@newsletters.newsblur.com>', + 'Date':'Fri, 26 Apr 2013 11:50:29 -0700', + 'Message-Id':'<517ACC75.5010709@newsletters.newsblur.com>', + 'body-plain':'Hi Alice,\n\nThis is Bob.\n\nI also attached a file.\n\nThanks,\nBob\n\nOn 04/26/2013 11:29 AM, Alice wrote:\n> Hi Bob,\n>\n> This is Alice. How are you doing?\n>\n> Thanks,\n> Alice\n\n', + 'Mime-Version':'1.0', + 'Received':'from [10.20.76.69] (Unknown [50.56.129.169]) by mxa.mailgun.org with ESMTP id 517acc75.4b341f0-worker2; Fri, 26 Apr 2013 18:50:29 -0000 (UTC)', + 'content-id-map':'{"": "attachment-1"}', + 'Sender':'bob@newsletters.newsblur.com', + 'timestamp':'1455054490', + 'message-headers':'[["Received", "by luna.mailgun.net with SMTP mgrt 8788212249833; Fri, 26 Apr 2013 18:50:30 +0000"], ["Received", "from [10.20.76.69] (Unknown [50.56.129.169]) by mxa.mailgun.org with ESMTP id 517acc75.4b341f0-worker2; Fri, 26 Apr 2013 18:50:29 -0000 (UTC)"], ["Message-Id", "<517ACC75.5010709@newsletters.newsblur.com>"], ["Date", "Fri, 26 Apr 2013 11:50:29 -0700"], ["From", "Bob "], ["User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130308 Thunderbird/17.0.4"], ["Mime-Version", "1.0"], ["To", "Alice "], ["Subject", "Re: Sample POST request"], ["References", "<517AC78B.5060404@newsletters.newsblur.com>"], ["In-Reply-To", "<517AC78B.5060404@newsletters.newsblur.com>"], ["X-Mailgun-Variables", "{\\"my_var_1\\": \\"Mailgun Variable #1\\", \\"my-var-2\\": \\"awesome\\"}"], ["Content-Type", "multipart/mixed; boundary=\\"------------020601070403020003080006\\""], ["Sender", "bob@newsletters.newsblur.com"]]', + 'stripped-text':'Hi Alice,\n\nThis is Bob.\n\nI also attached a file.', + 'recipient':'alice+555551235342@newsletters.newsblur.com', + 'sender':'bob@newsletters.newsblur.com', + 'X-Mailgun-Variables':'{"my_var_1": "Mailgun Variable #1", "my-var-2": "awesome"}', + 'token':'cb2ef40ca2fee03a099f7da78ca07384228f00f023026c77a4', + 'body-html':'\n \n \n \n \n
\n
Hi Alice,
\n

\n
\n
This is Bob. 
\n

\n I also attached a file.
\n
\n
\n
Thanks,
\n
Bob
\n
\n On 04/26/2013 11:29 AM, Alice wrote:
\n
\n
Hi\n Bob,\n
\n
\n This is Alice. How are you doing?\n
\n
\n Thanks,\n
\n Alice\n
\n
\n
\n \n\n', + 'References':'<517AC78B.5060404@newsletters.newsblur.com>', + 'signature':'0369fa4dcc7de7fac51f5bb408bd5c9daa8730e80d394e8a128658d74e669049', + 'Content-Type':'multipart/mixed; boundary="------------020601070403020003080006"', + 'Subject':'Re: Sample POST request' + } response = HttpResponse('OK') - return response \ No newline at end of file + + email_newsletter = EmailNewsletter() + story = email_newsletter.receive_newsletter(params) + + if not story: + raise Http404 + + return response + +def newsletter_story(request, story_hash): + story = MStory.objects.get(story_hash=story_hash) + story = Feed.format_story(story) + return HttpResponse(story['story_content']) \ No newline at end of file diff --git a/apps/rss_feeds/models.py b/apps/rss_feeds/models.py index 7141268c7..39f95a9ba 100644 --- a/apps/rss_feeds/models.py +++ b/apps/rss_feeds/models.py @@ -149,6 +149,10 @@ class Feed(models.Model): if not feed_address: feed_address = "" if not feed_link: feed_link = "" return hashlib.sha1(feed_address+feed_link).hexdigest() + + @property + def is_newsletter(self): + return self.feed_address.startswith('newsletter:') def canonical(self, full=False, include_favicon=True): feed = { @@ -166,6 +170,7 @@ class Feed(models.Model): 'min_to_decay': self.min_to_decay, 'subs': self.num_subscribers, 'is_push': self.is_push, + 'is_newsletter': self.is_newsletter, 'fetched_once': self.fetched_once, 'search_indexed': self.search_indexed, 'not_yet_fetched': not self.fetched_once, # Legacy. Doh. @@ -369,6 +374,8 @@ class Feed(models.Model): def get_feed_from_url(cls, url, create=True, aggressive=False, fetch=True, offset=0): feed = None + if url and url.startswith('newsletter:'): + return cls.objects.get(feed_address=url) if url and 'youtube.com/user/' in url: username = re.search('youtube.com/user/(\w+)', url).group(1) url = "http://gdata.youtube.com/feeds/base/users/%s/uploads" % username