"""
import collections as _collections
+from email.mime.message import MIMEMessage as _MIMEMessage
+from email.mime.multipart import MIMEMultipart as _MIMEMultipart
from email.utils import formataddr as _formataddr
import hashlib as _hashlib
import html.parser as _html_parser
from . import util as _util
-_feedparser.USER_AGENT = 'rss2email/{} +{}'.format(__version__, __url__)
+_USER_AGENT = 'rss2email/{} +{}'.format(__version__, __url__)
+_feedparser.USER_AGENT = _USER_AGENT
_urllib_request.install_opener(_urllib_request.build_opener())
_SOCKET_ERRORS = []
for e in ['error', 'herror', 'gaierror']:
# hints for value conversion
_boolean_attributes = [
+ 'digest',
'force_from',
'use_publisher_email',
'friendly_name',
extra_headers = _collections.OrderedDict((
('Date', self._get_entry_date(entry)),
('Message-ID', '<{}@dev.null.invalid>'.format(_uuid.uuid4())),
- ('User-Agent', 'rss2email'),
+ ('User-Agent', _USER_AGENT),
('X-RSS-Feed', self.url),
('X-RSS-ID', id_),
('X-RSS-URL', self._get_entry_link(entry)),
if not self.to:
raise _error.NoToEmailAddress(feed=self)
parsed = self._fetch()
+
+ if self.digest:
+ digest = self._new_digest()
+ seen = []
+
for (guid, id_, sender, message) in self._process(parsed):
_LOG.debug('new message: {}'.format(message['Subject']))
- if send:
- self._send(sender=sender, message=message)
+ if self.digest:
+ seen.append((guid, id_))
+ self._append_to_digest(digest=digest, message=message)
+ else:
+ if send:
+ self._send(sender=sender, message=message)
+ if guid not in self.seen:
+ self.seen[guid] = {}
+ self.seen[guid]['id'] = id_
+
+ if self.digest and seen:
+ self._send_digest(
+ digest=digest, seen=seen, sender=sender, send=send)
+
+ self.etag = parsed.get('etag', None)
+ self.modified = parsed.get('modified', None)
+
+ def _new_digest(self):
+ digest = _MIMEMultipart('digest')
+ digest['To'] = self.to # TODO: _Header(), _formataddr((recipient_name, recipient_addr))
+ digest['Subject'] = 'digest for {}'.format(self.name)
+ digest['Message-ID'] = '<{}@dev.null.invalid>'.format(_uuid.uuid4())
+ digest['User-Agent'] = _USER_AGENT
+ digest['X-RSS-Feed'] = self.url
+ return digest
+
+ def _append_to_digest(self, digest, message):
+ part = _MIMEMessage(message)
+ part.add_header('Content-Disposition', 'attachment')
+ digest.attach(part)
+
+ def _send_digest(self, digest, seen, sender, send=True):
+ """Send a digest message
+
+ The date is extracted from the last message in the digest
+ payload. We assume that this part exists. If you don't have
+ any messages in the digest, don't call this function.
+ """
+ digest['From'] = sender # TODO: _Header(), _formataddr()...
+ last_part = digest.get_payload()[-1]
+ last_message = last_part.get_payload()[0]
+ digest['Date'] = last_message['Date']
+
+ _LOG.debug('new digest for {}'.format(self))
+ if send:
+ self._send(sender=sender, message=digest)
+ for (guid, id_) in seen:
if guid not in self.seen:
self.seen[guid] = {}
self.seen[guid]['id'] = id_
- self.etag = parsed.get('etag', None)
- self.modified = parsed.get('modified', None)
--- /dev/null
+SENT BY: "gmane.mail.rss2email: W. Trevor King" <user@rss2email.invalid>
+Content-Type: multipart/digest; boundary="===============...=="
+MIME-Version: 1.0
+To: a@b.com
+Subject: digest for test
+Message-ID: <...@dev.null.invalid>
+User-Agent: rss2email/3.2 +https://github.com/wking/rss2email
+X-RSS-Feed: gmane/feed.rss
+From: "gmane.mail.rss2email: W. Trevor King" <user@rss2email.invalid>
+Date: Tue, 13 Nov 2012 14:36:22 -0000
+
+--===============...==
+Content-Type: message/rfc822
+MIME-Version: 1.0
+Content-Disposition: attachment
+
+MIME-Version: 1.0
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+From: "gmane.mail.rss2email: W. Trevor King" <user@rss2email.invalid>
+To: a@b.com
+Subject: Re: new maintainer and mailing list for rss2email
+Date: Mon, 12 Nov 2012 21:20:22 -0000
+Message-ID: <...@dev.null.invalid>
+User-Agent: rss2email/3.2 +https://github.com/wking/rss2email
+X-RSS-Feed: gmane/feed.rss
+X-RSS-ID: http://permalink.gmane.org/gmane.mail.rss2email/1
+X-RSS-URL: http://permalink.gmane.org/gmane.mail.rss2email/1
+
+Alrighty, this is the first email on the list and also my first
+
+ production mlmmj list, so I've CCed you both directly. Etienne, let
+ me know if you get the direct email but not the list email, in which
+ case I'll try and figure out what I've miss-configured ;). Lindsey,
+ I'll direct future rss2email stuff to my new list, so subscribe if
+ you're interested.
+
+ On Mon, Nov 12, 2012 at 06:17:50PM +0100, Etienne Millon wrote:
+
+ Wonderful. Let me know if you come up with anything during a
+ test-drive, and I'll get it in before the 3.0 release.
+
+
+ The 2.x config format is pure Python, which means the users can do
+ whatever they want there (including monkey-patching urllib2, changing
+ the rss2email version number, etc.). It's hard to imagine a robust
+ way to migrate everything a user may have done in there.
+
+
+ If you want to take a stab at it, I'll be happy to add it to a contrib
+ directory :).
+
+
+ Great :).
+
+ On Mon, Nov 12, 2012 at 01:48:13PM -0500, W. Trevor King wrote:
+
+ Done: https://github.com/wking/rss2email
+
+
+
+
+
+URL: http://permalink.gmane.org/gmane.mail.rss2email/1
+--===============...==
+Content-Type: message/rfc822
+MIME-Version: 1.0
+Content-Disposition: attachment
+
+MIME-Version: 1.0
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+From: "gmane.mail.rss2email: Etienne Millon" <user@rss2email.invalid>
+To: a@b.com
+Subject: Re: new maintainer and mailing list for rss2email
+Date: Tue, 13 Nov 2012 10:48:07 -0000
+Message-ID: <...@dev.null.invalid>
+User-Agent: rss2email/3.2 +https://github.com/wking/rss2email
+X-RSS-Feed: gmane/feed.rss
+X-RSS-ID: http://permalink.gmane.org/gmane.mail.rss2email/2
+X-RSS-URL: http://permalink.gmane.org/gmane.mail.rss2email/2
+
+* W. Trevor King <wking-vJI2gpByivqcqzYg7KEe8g< at >public.gmane.org> [121112 23:18]:
+
+ It seems to work, though it may have been grouped together with my
+ MDA. I'll tell you if I don't receive a mail where I'm not CCed.
+
+
+ We're finalizing a release ATM, so it will be the perfect time to try
+ a new rss2email release in a couple of months.
+
+
+ The idea is more to migrate the low hanging fruits (maybe 95% of
+ users) so that they don't lose their config. I was thinking to just
+ eval() the config file and output the relevant variables to the new
+ format. We'll see how it turns out :)
+
+
+ Do you prefer taking pull requests there or as a discussion on the
+ mailing list (git send-email style) ?
+
+
+
+
+
+URL: http://permalink.gmane.org/gmane.mail.rss2email/2
+--===============...==
+Content-Type: message/rfc822
+MIME-Version: 1.0
+Content-Disposition: attachment
+
+MIME-Version: 1.0
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+From: "gmane.mail.rss2email: W. Trevor King" <user@rss2email.invalid>
+To: a@b.com
+Subject: Re: new maintainer and mailing list for rss2email
+Date: Tue, 13 Nov 2012 12:20:20 -0000
+Message-ID: <...@dev.null.invalid>
+User-Agent: rss2email/3.2 +https://github.com/wking/rss2email
+X-RSS-Feed: gmane/feed.rss
+X-RSS-ID: http://permalink.gmane.org/gmane.mail.rss2email/3
+X-RSS-URL: http://permalink.gmane.org/gmane.mail.rss2email/3
+
+
+ send-email style, although I'll accept anything ;).
+
+
+
+
+
+URL: http://permalink.gmane.org/gmane.mail.rss2email/3
+--===============...==
+Content-Type: message/rfc822
+MIME-Version: 1.0
+Content-Disposition: attachment
+
+MIME-Version: 1.0
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+From: "gmane.mail.rss2email: Etienne Millon" <user@rss2email.invalid>
+To: a@b.com
+Subject: Re: new maintainer and mailing list for rss2email
+Date: Tue, 13 Nov 2012 12:42:13 -0000
+Message-ID: <...@dev.null.invalid>
+User-Agent: rss2email/3.2 +https://github.com/wking/rss2email
+X-RSS-Feed: gmane/feed.rss
+X-RSS-ID: http://permalink.gmane.org/gmane.mail.rss2email/4
+X-RSS-URL: http://permalink.gmane.org/gmane.mail.rss2email/4
+
+* W. Trevor King <wking-vJI2gpByivqcqzYg7KEe8g< at >public.gmane.org> [121113 13:21]:
+
+ Ack.
+
+ Also, confirming that the mailing list works.
+
+
+
+
+
+URL: http://permalink.gmane.org/gmane.mail.rss2email/4
+--===============...==
+Content-Type: message/rfc822
+MIME-Version: 1.0
+Content-Disposition: attachment
+
+MIME-Version: 1.0
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+From: "gmane.mail.rss2email: W. Trevor King" <user@rss2email.invalid>
+To: a@b.com
+Subject: split massive package into modules
+Date: Tue, 13 Nov 2012 14:36:22 -0000
+Message-ID: <...@dev.null.invalid>
+User-Agent: rss2email/3.2 +https://github.com/wking/rss2email
+X-RSS-Feed: gmane/feed.rss
+X-RSS-ID: http://permalink.gmane.org/gmane.mail.rss2email/5
+X-RSS-URL: http://permalink.gmane.org/gmane.mail.rss2email/5
+
+I just split the 1769-line rss2email.py module into a more manageable
+
+ package with sub-modules:
+
+ https://github.com/wking/rss2email/commit/066602efa088b4a89d67e23011613b4459db3c92
+
+
+
+
+
+URL: http://permalink.gmane.org/gmane.mail.rss2email/5
+--===============...==--
MESSAGE_ID_REGEXP = _re.compile(
'^Message-ID: <[^@]*@dev.null.invalid>$', _re.MULTILINE)
+BOUNDARY_REGEXP = _re.compile('===============[^=]+==')
class Send (list):
"""Cleanup dynamic portions of the generated email headers
>>> text = (
+ ... 'Content-Type: multipart/digest;\\n'
+ ... ' boundary="===============7509425281347501533=="\\n'
+ ... 'MIME-Version: 1.0\\n'
... 'Date: Tue, 23 Aug 2011 15:57:37 -0000\\n'
... 'Message-ID: <9dff03db-f5a7@dev.null.invalid>\\n'
... 'User-Agent: rss2email\\n'
... )
>>> print(clean_result(text).rstrip())
+ Content-Type: multipart/digest;
+ boundary="===============...=="
+ MIME-Version: 1.0
Date: Tue, 23 Aug 2011 15:57:37 -0000
Message-ID: <...@dev.null.invalid>
User-Agent: rss2email
"""
- return MESSAGE_ID_REGEXP.sub('Message-ID: <...@dev.null.invalid>', text)
+ for regexp,replacement in [
+ (MESSAGE_ID_REGEXP, 'Message-ID: <...@dev.null.invalid>'),
+ (BOUNDARY_REGEXP, '===============...=='),
+ ]:
+ text = regexp.sub(replacement, text)
+ return text
def test(dirname=None, config_path=None, force=False):
if dirname is None: