1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
#+date: <2022-06-22 Wed 00:00:00>
#+title: How to Receive a Daily Dose of Poetry via Automated Email
#+description: Instructional guide for scheduling and automating the distribution of classic and contemporary poetry in plaintext format to an electronic mail inbox using Python scripting and SMTP protocol.
#+slug: daily-poetry
#+filetags: :poetry:email:automation:
* Source Code
I don't want to bury the lede here, so if you'd like to see the full source code
I use to email myself plaintext poems daily, visit the repository:
[[https://git.cleberg.net/daily-poem.git][daily-poem.git]].
* My Daily Dose of Poetry
Most of my programming projects are small, random projects that are made
strictly to fix some small problem I have or enhance my quality of life.
In this case, I was looking for a simply and easy way to get a daily
dose of literature or poetry to read in the mornings.
However, I don't want to sign up for a random mailing list on just any
website. I also don't want to have to work to find the reading content
each morning, as I know I would simply give up and stop reading daily.
Thus, I found a way to deliver poetry to myself in plain-text format, on
a daily basis, and scheduled to deliver automatically.
* Prerequisites
This solution uses Python and email, so the following process requires
the following to be installed:
1. An SMTP server, which can be as easy as installing =mailutils= if
you're on a Debian-based distro.
2. Python (& pip!)
3. The following Python packages: =email=, =smtplib=, =json=, and
=requests=
* Breaking Down the Logic
I want to break down the logic for this program, as it's quite simple
and informational.
** Required Packages
This program starts with a simple import of the required packages, so I
wanted to explain why each package is used:
#+begin_src python
from email.mime.text import MIMEText # Required for translating MIMEText
import smtplib # Required to process the SMTP mail delivery
import json # Required to parse the poetry API results
import requests # Required to send out a request to the API
#+end_src
** Sending the API Request
Next, we need to actually send the API request. In my case, I'm calling
a random poem from the entire API. If you want, you can call specific
poems or authors from this API.
#+begin_src python
json_data = requests.get('https://poetrydb.org/random').json()
#+end_src
This gives us the following result in JSON:
#+begin_src json
[
{
"title": "Sonnet XXII: With Fools and Children",
"author": "Michael Drayton",
"lines": [
"To Folly",
"",
"With fools and children, good discretion bears;",
"Then, honest people, bear with Love and me,",
"Nor older yet, nor wiser made by years,",
"Amongst the rest of fools and children be;",
"Love, still a baby, plays with gauds and toys,",
"And, like a wanton, sports with every feather,",
"And idiots still are running after boys,",
"Then fools and children fitt'st to go together.",
"He still as young as when he first was born,",
"No wiser I than when as young as he;",
"You that behold us, laugh us not to scorn;",
"Give Nature thanks you are not such as we.",
"Yet fools and children sometimes tell in play",
"Some, wise in show, more fools indeed than they."
],
"linecount": "15"
}
]
#+end_src
** Parsing the API Results
In order to parse this into a readable format, we need to use the =json=
package and extract the fields we want. In the example below, I am
grabbing every field presented by the API.
For the actual poem content, we need to loop over each line in the
=lines= variable since each line is a separate string by default.
#+begin_quote
You /could/ also extract the title or author and make another call out
to the API to avoid having to build the plaintext poem with a loop, but
it just doesn't make sense to me to send multiple requests when we can
create a simple loop on our local machine to work with the data we
already have.
For
[[https://poetrydb.org/title/Sonnet%20XXII:%20With%20Fools%20and%20Children/lines.text][example]],
look at the raw data response of this link to see the poem's lines
returned in plaintext.
#+end_quote
#+begin_src python
title = json_data[0]['title']
author = json_data[0]['author']
line_count = json_data[0]['linecount']
lines = ''
for line in json_data[0]['lines']:
lines = lines + line + "\n"
#+end_src
** Composing the Email
Now that I have all the data I need, I just need to compose it into a
message and prepare the message metadata.
For my daily email, I want to see the title of the poem first, followed
by the author, then a blank line, and finally the full poem. This code
snippet combines that data and packages it into a MIMEText container,
ready to be emailed.
#+begin_src python
msg_body = title + "\n" + author + "\n\n" + lines
msg = MIMEText(msg_body)
#+end_src
Before we send the email, we need to prepare the metadata (subject,
from, to, etc.):
#+begin_src python
sender_email = 'example@server.local'
recipient_emails = ['user@example.com']
msg['Subject'] = 'Your Daily Poem (' + line_count + ' lines)'
msg['From'] = sender_email
msg['To'] = recipient_email
#+end_src
** Sending the Email
Now that I have everything ready to be emailed, the last step is to
simply connect to an SMTP server and send the email out to the
recipients. In my case, I installed =mailutils= on Ubuntu and let my
SMTP server be =localhost=.
#+begin_src python
smtp_server = 'localhost'
s = smtplib.SMTP(smtp_server)
s.sendmail(sender_email, recipient_emails, msg.as_string())
s.quit()
#+end_src
* The Result!
Instead of including a screenshot, I've copied the contents of the email
that was delivered to my inbox below since I set this process up in
plaintext format.
#+begin_src txt
Date: Wed, 22 Jun 2022 14:37:19 +0000 (UTC)
From: REDACTED
To: REDACTED
Subject: Your Daily Poem (36 lines)
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset=utf-8
Sonnet XXII: With Fools and Children
Michael Drayton
With fools and children, good discretion bears;
Then, honest people, bear with Love and me,
Nor older yet, nor wiser made by years,
Amongst the rest of fools and children be;
Love, still a baby, plays with gauds and toys,
And, like a wanton, sports with every feather,
And idiots still are running after boys,
Then fools and children fitt'st to go together.
He still as young as when he first was born,
No wiser I than when as young as he;
You that behold us, laugh us not to scorn;
Give Nature thanks you are not such as we.
Yet fools and children sometimes tell in play
Some, wise in show, more fools indeed than they.
#+end_src
* Scheduling the Daily Email
Last, but not least, is scheduling this Python script with =crontab=. To
schedule a script to run daily, you can add it to the =crontab= file. To
do this, open =crontab= in editing mode:
#+begin_src sh
crontab -e
#+end_src
In the file, simply paste the following snippet at the bottom of the
file and ensure that the file path is correctly pointing to wherever you
saved your Python script:
#+begin_src config
0 8 ** ** ** python3 /home/<your_user>/dailypoem/main.py
#+end_src
We have now set up the script and scheduled it to run daily at 08:00!
|