From 91d5a1176b5c9782656913c0c9f617a3e7986c29 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Sun, 31 Jan 2021 22:04:19 +0100 Subject: [PATCH] Migrated newsletter automation article --- static/img/newsletter-1.png | Bin 0 -> 8825 bytes ...wsletters-from-RSS-feeds-with-Platypush.md | 315 ++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 static/img/newsletter-1.png create mode 100644 static/pages/Deliver-customized-newsletters-from-RSS-feeds-with-Platypush.md diff --git a/static/img/newsletter-1.png b/static/img/newsletter-1.png new file mode 100644 index 0000000000000000000000000000000000000000..cb8867ec4666c41cfc23e04b7bc529f50e208caa GIT binary patch literal 8825 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

|* zHkLy7k$kuadlMO@-~Wx4@%urI16<51B zW?c6bYq|RqBq#nqz-7Xl zA5p}eWL;1&1v_;F57KYb`&VVLP3F*>c!GRa=keaKvO8R6x;uI%%Yf*GR<^cBc$!6v z8JLSUU7p${WNg%Pk|811D(mJ+#fE|!;8IWtUw3L$_c>^OFXfqO_3F>ytENAy6C5w0e E2g!~)mjD0& literal 0 HcmV?d00001 diff --git a/static/pages/Deliver-customized-newsletters-from-RSS-feeds-with-Platypush.md b/static/pages/Deliver-customized-newsletters-from-RSS-feeds-with-Platypush.md new file mode 100644 index 0000000..fe2e733 --- /dev/null +++ b/static/pages/Deliver-customized-newsletters-from-RSS-feeds-with-Platypush.md @@ -0,0 +1,315 @@ +[//]: # (title: Deliverd customized newsletters from RSS feeds with Platypush) +[//]: # (description: Use the RSS and email integrations to created automated newsletters.) +[//]: # (image: /img/extension-1.png) +[//]: # (author: Fabio Manganiello ) +[//]: # (published: 2020-09-06) + +I’ve always been a supporter of well-curated newsletters. They give me an opportunity to get a good overview of what +happened in the fields I follow within a span of a day, a week or a month. However, not all the newsletters fit this +category. Some don’t think three times before selling email addresses to 3rd-parties — and within the blink of an eye +your mailbox can easily get flooded with messages that you didn’t request. Others may sign up your address for other +services or newsletters as well, and often they don’t often much granularity to configure which communications you want +to receive. Even in the best-case scenario, the most privacy-savvy user may still think twice before signing up for a +newsletter — you’re giving your personal email address to someone else you don’t necessarily trust, implying “yes, this +is my address and I’m interested in this subject”. Additionally, most of the newsletters spice up their URLs with +tracking parameters, so they can easily measure user engagement — something you may not necessarily be happy with. +Moreover, the customization junkie may also have a valid use case for a more finely tuned selection of content in his +newsletter — you may want to group some sources together into the same daily/weekly email, or you may be interested only +in some particular subset of the subjects covered by a newsletter, filtering out those that aren’t relevant, or +customize the style of the digest that gets delivered. Finally, a fully automated way to deliver newsletters through 5 +lines of code and the tuning of a couple of parameters is the nirvana for many companies of every size out there. + +## Feed up the newsletter + +Those who read my articles in the past may know that I’m an avid consumer of RSS feeds. Despite being a 21-year-old +technology, they do their job very well when it comes to deliver the information that matters without all the noise and +trackers, and they provide a very high level of integration being simple XML documents. However, in spite of all the +effort I put to be up-to-date with all my sources, a lot of potentially interesting content inevitably slips through — +and that’s where newsletters step in, as they filter and group together all the content that was generated in a given +time frame and periodically deliver it to your inbox. + +My ideal solution would be something that combines the best aspects of both the worlds: the flexibility of an RSS +subscription, combined with a flexible way of filtering and aggregating content and sources, and get the full package +delivered at my door in whichever format I like (HTML, PDF, MOBI…). In this article I’m going to show how to achieve +this goal with a few tools: + +- One or more sources that you want to track and that support RSS feeds (in this example I’ll use the [MIT Technology + Review RSS feed](https://www.technologyreview.com/feed/), but the procedure works for any RSS feed). + +- An email address. + +- [Platypush](https://git.platypush.tech/platypush/platypush) to do the heavy-lifting job — monitor the RSS sources at + custom intervals, trigger events when a source has some new content, create a digest out of the new content, and + deliver the full package to a list of email addresses. + +Let’s cover these points step by step. + +## Installing and configuring Platypush + +We’ll be using the [`http.poll`](https://platypush.readthedocs.io/en/latest/platypush/backend/http.poll.html) backend +configured with one or more `RssUpdate` objects to poll our RSS sources at regular intervals and create the digests, and +either the [`mail.smtp`](https://platypush.readthedocs.io/en/latest/platypush/plugins/mail.smtp.html) plugin or the +[`google.mail`](https://platypush.readthedocs.io/en/latest/platypush/plugins/google.mail.html) plugin to send the +digests to our email. + +You can install Platypush on any device where you want to run your logic — a RaspberryPi, an old laptop, a cloud node, +and so on. We will install the base package with the `rss` module. Optionally, you can install it with the `pdf` module +as well (if you want to export your digests also to PDF) or the `google` module (if you want to send the newsletter from +a GMail address instead of an SMTP server). + +The first option is to install the latest stable version through `pip`: + +```shell +[sudo] pip install 'platypush[rss,pdf,google]' +``` + +The other option is to install the latest git version: + +```shell +git clone https://git.platypush.tech/platypush/platypush +cd platypush +[sudo] pip install '.[rss,pdf,google]' +``` + +## Monitoring your RSS feeds + +Once the software is installed, create the configuration file `~/.config/platypush/config.yaml` if it doesn't exist +already and add the configuration for the RSS monitor: + +```yaml +# Generic HTTP endpoint monitor +backend.http.poll: + requests: + # Add a new RSS feed to the pool + - type: platypush.backend.http.request.rss.RssUpdates + # URL to the RSS feed + url: https://www.technologyreview.com/feed/ + # Title of the feed (shown in the head of the digest) + title: MIT Technology Review + # How often we should monitor this source (24*60*60 secs = once a day) + poll_seconds: 86400 + # Format of the digest (HTML or PDF) + digest_format: html +``` + +You can also add more sources to the `http.poll` `requests` object, each with its own configuration. Also, you can +customize the style of your digest by passing some valid CSS to these configuration attributes: + +```yaml +# Style of the body element +body_style: 'font-size: 20px; font-family: "Merriweather", Georgia, "Times New Roman", Times, serif' + +# Style of the main title +title_style: 'margin-top: 30px' + +# Style of the subtitle +subtitle_style: 'margin-top: 10px; page-break-after: always' + +# Style of the article titles +article_title_style: 'font-size: 1.6em; margin-top: 1em; padding-top: 1em; border-top: 1px solid #999' + +# Style of the article link +article_link_style: 'color: #555; text-decoration: none; border-bottom: 1px dotted font-size: 0.8em' + +# Style of the article content +article_content_style: 'font-size: 0.8em' +``` + +The `digest_format` attribute determines the output format of your digest - you may want to choose `html` if you want to +deliver a summary of the articles in a newsletter, or pdf if you want instead to deliver the full content of each item +as an attachment to an email address. Bonus point: since you can send PDFs to a Kindle if you +[configured an email address](https://www.amazon.com/gp/sendtokindle/email), +this mechanism allows you to deliver the full digest of your RSS feeds to your Kindle's email address. + +The [`RssUpdates`](https://github.com/BlackLight/platypush/blob/ac02becba80fafce39d5bbcfc682f7a8fe46f529/platypush/backend/http/request/rss/__init__.py#L21) +object also provides native integration with the [Mercury Parser API](https://github.com/postlight/mercury-parser-api) +to automatically scrape the content of a web page - I covered some of these concepts in +my [previous article](https://blog.platypush.tech/article/Deliver-articles-to-your-favourite-e-reader-using-Platypush) +on how to parse RSS feeds and send the PDF digest to your e-reader. The same mechanism works well for newsletters too. +If you want to parse the content of the newsletter as well, all you have to do is configure +the [`http.webpage`](https://platypush.readthedocs.io/en/latest/platypush/plugins/http.webpage.html) Platypush +plugin. Since the Mercury API doesn't provide a Python binding, this requires a couple of JavaScript dependencies: + +```shell +# Install Node and NPM, e.g. on Debian: +apt-get install nodejs npm + +# Install the Mercury Parser API +npm install [-g] @postlight/mercury-parser + +# Make sure that the Platypush PDF module dependencies +# are installed if you plan HTML->PDF conversion +pip install 'platypush[pdf]' +``` + +Then, if you want to parse the full content of the items and generate a PDF digest out of them, change your `http.poll` +configuration to something like this: + +```yaml +backend.http.poll: + requests: + - type: platypush.backend.http.request.rss.RssUpdates + url: https://www.technologyreview.com/feed/ + title: MIT Technology Review + poll_seconds: 86400 + # PDF digest format + digest_format: pdf + # Extract the full content of the items + extract_content: True +``` + +**WARNING**: Extracting the full content of the articles in an RSS feed has two limitations — a practical one and a +legal one: + +- Some websites may require user login before displaying the full content of an article. Some websites perform such + checks client-side — and the parser API can usually circumvent them, especially if the full content of an article is + actually just hidden behind a client-side paywall. Some websites, however, implement their user checks server-side too + before sending the content to the client — and in those cases the parser API may return only a part of the content or + no content at all. + +- Always keep in mind that parsing the full content of an article behind a paywall may represent a violation of + intellectual property under some jurisdictions, so make sure to do it only for content that is either free or that you + have to permission to scrape. + +## Configuring the mail delivery + +When new content is published on a subscribed RSS feed Platypush will generate +a [NewFeedEvent](https://platypush.readthedocs.io/en/latest/platypush/events/http.rss.html) and it should create a copy +of the digest under `~/.local/share/platypush/feeds/cache/{date:time}_{feed-title}.[html|pdf]`. The `NewFeedEvent` in +particular is the link you need to create your custom logic that sends an email to a list of addresses when new content +is available. + +First, configure the Platypush mail plugin you prefer. When it comes to sending emails you primarily have two options: + +- The [`mail.smtp`](https://platypush.readthedocs.io/en/latest/platypush/plugins/mail.smtp.html) plugin — if you want to + send emails directly through an SMTP server. Platypush configuration: + +```yaml +mail.smtp: + username: you@gmail.com + password: your-pass + server: smtp.gmail.com + port: 465 + ssl: True +``` + +- The [`google.mail`](https://platypush.readthedocs.io/en/latest/platypush/plugins/google.mail.html) plugin — if you + want to use the native GMail API to send emails. If that is the case then first make sure that you have the + dependencies for the Platypush Google module installed: + +```shell +[sudo] pip install 'platypush[google]' +``` + +In this case you’ll also have to create a project on +the [Google Developers console](https://console.developers.google.com/) and download the OAuth credentials: + +- Click on “Credentials” from the context menu > OAuth Client ID. + +- Once generated, you can see your new credentials in the “OAuth 2.0 client IDs” section. Click on the “Download” icon to save them to a JSON file. + +- Copy the file to your Platypush device/server under e.g. `~/.credentials/google/client_secret.json`. + +- Run the following command on the device to authorize the application: + +```shell +python -m platypush.plugins.google.credentials \ + "https://www.googleapis.com/auth/gmail.modify" \ + ~/.credentials/google/client_secret.json \ + --noauth_local_webserver +``` + +At this point the GMail delivery is ready to be used by your Platypush automation. + +## Connecting the dots + +Now that both the RSS parsing logic and the mail integration are in place, we can glue them together through the +[`NewFeedEvent`](https://platypush.readthedocs.io/en/latest/platypush/events/http.rss.html) event. The new advised way +to configure events in Platypush is through native Python scripts - the custom YAML-based syntax for events and +procedure was becoming too cumbersome to maintain and write (although it’s still supported), and I feel like going back +to a clean and simple Python API may be a better option. + +Create and initialize the Platypush scripts directory, if it doesn’t exist already: + +```shell +mkdir -p ~/.config/platypush/scripts +cd ~/.config/platypush/scripts + +# Make sure that the scripts module is initialized +touch __init__.py +``` + +Then, create a new hook on `NewFeedEvent`: + +```shell +$EDITOR rss_news.py +``` + +```python +import os +from typing import List + +from platypush.event.hook import hook +from platypush.message.event.http.rss import NewFeedEvent +from platypush.utils import run + +# Path to your mailing list - a text file with one address per line +maillist = os.path.expanduser('~/.mail.list') + +def get_addresses() -> List[str]: + with open(maillist, 'r') as f: + return [addr.strip() for addr in f.readlines() + if addr.strip() and not addr.strip().startswith('#')] + + +# This hook matches: +# - event_type=NewFeedEvent +# - digest_format='html' +# - source_title='MIT Technology Review' +@hook(NewFeedEvent, digest_format='html', source_title='MIT Technology Review') +def send_mit_rss_feed_digest(event: NewFeedEvent, **_): + # The digest output file is stored in event.args['digest_filename'] + with open(event.args['digest_filename'], 'r') as f: + run(action='mail.smtp.send', + from_='you@yourdomain.com', + to=get_addresses(), + subject=f'{event.args.get("source_title")} feed digest', + body=f.read(), + body_type='html') + +# Or, if you opted for the native GMail plugin you may want to go for: + +@hook(NewFeedEvent, digest_format='html', source_title='MIT Technology Review') +def send_mit_rss_feed_digest(event: NewFeedEvent, **_): + # The digest output file is stored in event.args['digest_filename'] + with open(event.args['digest_filename'], 'r') as f: + run(action='google.mail.compose', + sender='you@gmail.com', + to=get_addresses(), + subject=f'{event.args.get("source_title")} feed digest', + body=f.read()) + +# If instead you want to send the digest in PDF format as an attachment: + +@hook(NewFeedEvent, digest_format='html', source_title='MIT Technology Review') +def send_mit_rss_feed_digest(event: NewFeedEvent, **_): + # mail.smtp plugin case + run(action='mail.smtp.send', + from_='you@yourdomain.com', + to=get_addresses(), + subject=f'{event.args.get("source_title")} feed digest', + body='', + attachments=[event.args['digest_filename']]) + + # google.mail case + run(action='google.mail.compose', + sender='you@gmail.com', + to=get_addresses(), + subject=f'{event.args.get("source_title")} feed digest', + body='', + files=[event.args['digest_filename']]) +``` + +Finally, create your `~/.mail.list` file with one destination email address per line and start platypush either from the +command line or as a service. You should receive your email with the first batch of articles shortly after startup, and +you'll receive more items if a new batch is available after the `poll_seconds` configured period.