Skip to main content

max / makenotwork

9.5 KB · 341 lines History Blame Raw
1 //! Purchase, subscription, and Fan+ programme email templates.
2
3 use crate::email::{Audience, Delivery, EmailClass, EmailClient, OperationalKind};
4 use crate::error::Result;
5
6 impl EmailClient {
7 /// Send a purchase confirmation email
8 pub async fn send_purchase_confirmation(
9 &self,
10 to_email: &str,
11 to_name: Option<&str>,
12 item_title: &str,
13 price: &str,
14 ) -> Result<()> {
15 let subject = "Your purchase is confirmed";
16 let body = format!(
17 r"Hi{name},
18
19 Your purchase of {item} ({price}) is confirmed.
20
21 You can access your purchase from your library at any time.
22
23 - Makenotwork",
24 name = crate::email::greeting(to_name),
25 item = item_title,
26 price = price
27 );
28
29 self.dispatch(
30 EmailClass::Operational(OperationalKind::PurchaseReceipt),
31 Audience::Address(to_email),
32 subject,
33 &body,
34 Delivery::default(),
35 )
36 .await
37 }
38
39 /// Send a guest purchase confirmation email with download and claim links.
40 pub async fn send_guest_purchase_confirmation(
41 &self,
42 to_email: &str,
43 item_title: &str,
44 price: &str,
45 download_url: &str,
46 claim_url: &str,
47 ) -> Result<()> {
48 let subject = "Your purchase is confirmed";
49 let body = format!(
50 r"Hi,
51
52 Your purchase of {item_title} ({price}) is confirmed.
53
54 Download your purchase:
55 {download_url}
56
57 This link works without an account. You can download at any time.
58
59 Want to keep all your purchases in one place? Create a Makenotwork account with this email address and your purchase will appear in your library automatically. Or use this link to claim it to an existing account:
60 {claim_url}
61
62 - Makenotwork",
63 );
64
65 self.dispatch(
66 EmailClass::Operational(OperationalKind::PurchaseReceipt),
67 Audience::Address(to_email),
68 subject,
69 &body,
70 Delivery::default(),
71 )
72 .await
73 }
74
75 /// Send a subscription started email
76 pub async fn send_subscription_started(
77 &self,
78 to_email: &str,
79 to_name: Option<&str>,
80 tier_name: &str,
81 project_title: &str,
82 price: &str,
83 ) -> Result<()> {
84 let subject = &format!("You're subscribed to {project_title}");
85 let body = format!(
86 r"Hi{name},
87
88 You're now subscribed to {project} ({tier} - {price}/mo).
89
90 You have access to all content included in this tier. Your subscription will renew automatically each month.
91
92 - Makenotwork",
93 name = crate::email::greeting(to_name),
94 project = project_title,
95 tier = tier_name,
96 price = price
97 );
98
99 self.dispatch(
100 EmailClass::Operational(OperationalKind::SubscriptionBilling),
101 Audience::Address(to_email),
102 subject,
103 &body,
104 Delivery::default(),
105 )
106 .await
107 }
108
109 /// Send a subscription cancelled email
110 pub async fn send_subscription_cancelled(
111 &self,
112 to_email: &str,
113 to_name: Option<&str>,
114 tier_name: &str,
115 project_title: &str,
116 ) -> Result<()> {
117 let subject = "Your subscription has been cancelled";
118 let body = format!(
119 r"Hi{name},
120
121 Your subscription to {project} ({tier}) has been cancelled.
122
123 You will retain access until the end of your current billing period.
124
125 - Makenotwork",
126 name = crate::email::greeting(to_name),
127 project = project_title,
128 tier = tier_name
129 );
130
131 self.dispatch(
132 EmailClass::Operational(OperationalKind::SubscriptionBilling),
133 Audience::Address(to_email),
134 subject,
135 &body,
136 Delivery::default(),
137 )
138 .await
139 }
140
141 /// Send a subscription renewed email
142 pub async fn send_subscription_renewed(
143 &self,
144 to_email: &str,
145 to_name: Option<&str>,
146 tier_name: &str,
147 price: &str,
148 ) -> Result<()> {
149 let subject = "Your subscription has been renewed";
150 let body = format!(
151 r"Hi{name},
152
153 Your subscription ({tier} - {price}/mo) has been renewed for another month.
154
155 - Makenotwork",
156 name = crate::email::greeting(to_name),
157 tier = tier_name,
158 price = price
159 );
160
161 self.dispatch(
162 EmailClass::Operational(OperationalKind::SubscriptionBilling),
163 Audience::Address(to_email),
164 subject,
165 &body,
166 Delivery::default(),
167 )
168 .await
169 }
170
171 /// Send a Fan+ welcome email after subscription starts.
172 pub async fn send_fan_plus_welcome(&self, to_email: &str, to_name: Option<&str>) -> Result<()> {
173 let subject = "Welcome to Fan+";
174 let body = format!(
175 r"Hi{name},
176
177 You're now a Fan+ member.
178
179 Each month you'll receive a $5 credit code you can use toward any purchase on the platform. Your first credit will arrive with your next billing cycle.
180
181 Thank you for supporting Makenotwork.
182
183 - Makenotwork",
184 name = crate::email::greeting(to_name),
185 );
186
187 self.dispatch(
188 EmailClass::Operational(OperationalKind::FanPlus),
189 Audience::Address(to_email),
190 subject,
191 &body,
192 Delivery::default(),
193 )
194 .await
195 }
196
197 /// Send a Fan+ monthly credit code email.
198 pub async fn send_fan_plus_credit(
199 &self,
200 to_email: &str,
201 to_name: Option<&str>,
202 code: &str,
203 expiry: Option<&chrono::DateTime<chrono::Utc>>,
204 ) -> Result<()> {
205 let subject = "Your Fan+ monthly credit";
206 let expiry_line = expiry
207 .map(|d| format!("\n\nThis code expires on {}.", d.format("%B %-d, %Y")))
208 .unwrap_or_default();
209 let body = format!(
210 r"Hi{name},
211
212 Your $5 Fan+ credit for this month is ready.
213
214 Code: {code}
215
216 Enter this code at checkout to apply it toward any purchase on the platform.{expiry}
217
218 - Makenotwork",
219 name = crate::email::greeting(to_name),
220 code = code,
221 expiry = expiry_line,
222 );
223
224 self.dispatch(
225 EmailClass::Operational(OperationalKind::FanPlus),
226 Audience::Address(to_email),
227 subject,
228 &body,
229 Delivery::default(),
230 )
231 .await
232 }
233
234 /// Notify a creator that their backgrounded content export is ready, with a
235 /// time-limited download link. The export ZIP is built off the request path,
236 /// so the link arrives by email rather than inline in the dashboard.
237 pub async fn send_content_export_ready(
238 &self,
239 to_email: &str,
240 to_name: Option<&str>,
241 download_url: &str,
242 ) -> Result<()> {
243 let subject = "Your content export is ready";
244 let body = format!(
245 r"Hi{name},
246
247 Your content export is ready to download:
248
249 {url}
250
251 This link expires in one hour. If it expires before you download, start a new export from your dashboard.
252
253 - Makenotwork",
254 name = crate::email::greeting(to_name),
255 url = download_url,
256 );
257
258 self.dispatch(
259 EmailClass::Operational(OperationalKind::ContentExport),
260 Audience::Address(to_email),
261 subject,
262 &body,
263 Delivery::default(),
264 )
265 .await
266 }
267
268 /// Notify a creator that their content export could not be completed, so a
269 /// silently-failed background job doesn't leave them waiting indefinitely.
270 pub async fn send_content_export_failed(
271 &self,
272 to_email: &str,
273 to_name: Option<&str>,
274 reason: Option<&str>,
275 ) -> Result<()> {
276 let subject = "Your content export could not be completed";
277 let reason_line = reason.map(|r| format!("\n\n{r}")).unwrap_or_default();
278 let body = format!(
279 r"Hi{name},
280
281 We were unable to finish preparing your content export. No changes were made to your content.{reason}
282
283 Please try again from your dashboard; if it keeps failing, contact info@makenot.work.
284
285 - Makenotwork",
286 name = crate::email::greeting(to_name),
287 reason = reason_line,
288 );
289
290 self.dispatch(
291 EmailClass::Operational(OperationalKind::ContentExport),
292 Audience::Address(to_email),
293 subject,
294 &body,
295 Delivery::default(),
296 )
297 .await
298 }
299
300 /// Send a Fan+ cancellation email.
301 pub async fn send_fan_plus_cancelled(
302 &self,
303 to_email: &str,
304 to_name: Option<&str>,
305 period_end: Option<&chrono::DateTime<chrono::Utc>>,
306 ) -> Result<()> {
307 let subject = "Your Fan+ membership has been cancelled";
308 let access_line = period_end.map_or_else(
309 || "Your Fan+ benefits have ended.".to_string(),
310 |d| {
311 format!(
312 "You will retain your Fan+ benefits until {}.",
313 d.format("%B %-d, %Y")
314 )
315 },
316 );
317 let body = format!(
318 r"Hi{name},
319
320 Your Fan+ membership has been cancelled.
321
322 {access}
323
324 Any unused credit codes remain valid until their expiry date.
325
326 - Makenotwork",
327 name = crate::email::greeting(to_name),
328 access = access_line,
329 );
330
331 self.dispatch(
332 EmailClass::Operational(OperationalKind::FanPlus),
333 Audience::Address(to_email),
334 subject,
335 &body,
336 Delivery::default(),
337 )
338 .await
339 }
340 }
341