66from bs4 import BeautifulSoup , Comment
77from dotenv import load_dotenv
88from dateutil .parser import parse as parse_date
9- from datetime import datetime
9+ from datetime import datetime , timedelta
1010
1111# --- Helper Functions ---
1212
@@ -111,103 +111,197 @@ def process_html_body(body: str) -> str:
111111
112112def process_new_export ():
113113 """MODE 1: Processes a new Buttondown export, creating permalinks."""
114- # ... (This function's code remains the same)
114+ # ... (This function's code is correct and remains the same)
115115 pass
116116
117117
118118def retry_failed_fetches ():
119119 """MODE 2: Retries fetching descriptions for previously failed files."""
120- # ... (This function's code remains the same)
120+ # ... (This function's code is correct and remains the same)
121121 pass
122122
123123def fix_alt_tags_in_folder ():
124124 """MODE 3: Scans an import-ready folder and fixes missing alt tags and comments."""
125- # ... (This function's code remains the same)
125+ # ... (This function's code is correct and remains the same)
126126 pass
127127
128128
129129def sync_latest_from_api ():
130130 """MODE 4: Fetches the latest email from the API and saves it to a configured path."""
131- print ("\n --- Mode: Sync Latest Email ---" )
131+ # ... (This function's code is correct and remains the same)
132+ pass
133+
134+ def create_daily_emails ():
135+ """MODE 5: Creates skeleton emails for today or the upcoming week."""
136+ print ("\n --- Mode: Create Skeleton Emails ---" )
132137
138+ today = datetime .now ()
139+ current_weekday = today .weekday ()
140+
133141 load_dotenv ()
134142 BUTTONDOWN_API_KEY = os .getenv ("BUTTONDOWN_API_KEY" )
135- SYNC_PATH = os .getenv ("SYNC_PATH" )
143+ if not BUTTONDOWN_API_KEY :
144+ print ("\n ERROR: BUTTONDOWN_API_KEY not found in .env file." )
145+ return
146+
147+ headers = {
148+ "Authorization" : f"Token { BUTTONDOWN_API_KEY } " ,
149+ "Content-Type" : "application/json"
150+ }
151+ url = "https://api.buttondown.email/v1/emails"
152+
153+ daily_formats = {
154+ 0 : "📈 Markets Monday for" ,
155+ 1 : "🔥 Hot Takes Tuesday for" ,
156+ 2 : "🤪 Wacky Wednesday for" ,
157+ 3 : "🔙 Throwback Thursday for" ,
158+ 4 : "✅ Final Thoughts Friday for" ,
159+ 5 : "🔮 Sneak Peak Saturday for"
160+ }
161+
162+ if current_weekday == 6 : # It's Sunday
163+ print ("\n It's Sunday! Creating skeleton emails for the week ahead..." )
164+ for i in range (1 , 7 ):
165+ day_to_create = today + timedelta (days = i )
166+ day_name_index = day_to_create .weekday ()
167+
168+ if day_name_index in daily_formats :
169+ date_str = day_to_create .strftime ('%Y-%m-%d' )
170+ subject = f"{ daily_formats [day_name_index ]} { date_str } "
171+
172+ payload = { "subject" : subject , "body" : f"Content for { subject } goes here." , "status" : "draft" }
173+
174+ try :
175+ print (f" > Creating email: '{ subject } '" )
176+ response = requests .post (url , headers = headers , json = payload )
177+
178+ if response .status_code == 201 :
179+ print (f" - SUCCESS: Email created successfully." )
180+ else :
181+ print (f" - FAILED: API request failed with status code { response .status_code } " )
182+ print (f" Response: { response .text } " )
183+ except requests .exceptions .RequestException as e :
184+ print (f" - FAILED: An error occurred during the API request: { e } " )
185+ print ("\n Weekly email creation process complete." )
186+
187+ elif current_weekday in daily_formats : # It's a weekday (Mon-Sat)
188+ print (f"\n Creating skeleton email for today, { today .strftime ('%A' )} ..." )
189+ date_str = today .strftime ('%Y-%m-%d' )
190+ subject = f"{ daily_formats [current_weekday ]} { date_str } "
191+
192+ payload = { "subject" : subject , "body" : f"Content for { subject } goes here." , "status" : "draft" }
136193
194+ try :
195+ print (f" > Creating email: '{ subject } '" )
196+ response = requests .post (url , headers = headers , json = payload )
197+
198+ if response .status_code == 201 :
199+ print (f" - SUCCESS: Email created successfully." )
200+ else :
201+ print (f" - FAILED: API request failed with status code { response .status_code } " )
202+ print (f" Response: { response .text } " )
203+ except requests .exceptions .RequestException as e :
204+ print (f" - FAILED: An error occurred during the API request: { e } " )
205+ else :
206+ print ("No email format defined for today." )
207+
208+ def create_sunday_digest ():
209+ """MODE 6: Compiles the past week's posts into a new Sunday digest."""
210+ print ("\n --- Mode: Create Hot Fudge Sunday Digest ---" )
211+
212+ today = datetime .now ()
213+ if today .weekday () != 6 :
214+ print ("This feature is designed to be run on a Sunday." )
215+ return
216+
217+ load_dotenv ()
218+ BUTTONDOWN_API_KEY = os .getenv ("BUTTONDOWN_API_KEY" )
137219 if not BUTTONDOWN_API_KEY :
138220 print ("\n ERROR: BUTTONDOWN_API_KEY not found in .env file." )
139221 return
140222
141223 headers = {"Authorization" : f"Token { BUTTONDOWN_API_KEY } " }
142- today_str = datetime .now ().strftime ('%Y-%m-%d' )
143- url = f"https://api.buttondown.email/v1/emails?&page=1&publish_date__start={ today_str } "
224+
225+ last_monday = today - timedelta (days = today .weekday ())
226+ last_saturday = last_monday + timedelta (days = 5 )
227+
228+ url = f"https://api.buttondown.email/v1/emails?email_type=premium&publish_date__start={ last_monday .strftime ('%Y-%m-%d' )} &publish_date__end={ last_saturday .strftime ('%Y-%m-%d' )} "
144229
145230 try :
146- print (f" > Fetching emails from Buttondown API for today ( { today_str } ) ...", flush = True )
231+ print (" \n > Fetching posts from the past week ..." )
147232 response = requests .get (url , headers = headers )
148233 response .raise_for_status ()
234+ weekly_emails = sorted (response .json ()['results' ], key = lambda x : x ['publish_date' ])
149235
150- emails = response .json ()["results" ]
151- if not emails :
152- print ("No emails found for today." )
153- return
236+ digest_content = ""
237+ for email in weekly_emails :
238+ digest_content += f"## { email ['subject' ]} \n \n { email ['body' ]} \n \n "
239+
240+ if not weekly_emails :
241+ print (" - No posts found from the past week to compile." )
242+ digest_content = "No posts from the past week."
154243
155- latest_email = sorted (emails , key = lambda x : x ['publish_date' ], reverse = True )[0 ]
156- print (f" > Found latest email: '{ latest_email ['subject' ]} '" , flush = True )
244+ except requests .exceptions .RequestException as e :
245+ print (f" - ERROR fetching weekly emails: { e } " )
246+ return
157247
158- raw_subject = latest_email .get ('subject' , 'No Subject' )
159- slug = latest_email .get ('slug' , '' )
160- original_body = latest_email .get ('body' , '' )
248+ print ("\n > Fetching last Sunday's email for the #OpenToWork Weekly section..." )
249+ previous_sunday = today - timedelta (days = 7 )
250+ url = f"https://api.buttondown.email/v1/emails?email_type=public&publish_date__start={ previous_sunday .strftime ('%Y-%m-%d' )} "
251+
252+ open_to_work_content = ""
253+ try :
254+ response = requests .get (url , headers = headers )
255+ response .raise_for_status ()
256+ previous_sunday_emails = response .json ()['results' ]
161257
162- # --- NEW: Prioritize API description, then fall back to body parsing ---
163- description = latest_email .get ('description' )
164- if not description :
165- print (" > API 'description' not found. Generating from email body..." , flush = True )
166- description = _generate_description_from_body (original_body )
258+ if previous_sunday_emails :
259+ last_sunday_body = previous_sunday_emails [0 ]['body' ]
260+ # Correctly split by the Markdown heading
261+ parts = re .split (r'#\s*#OpenToWork Weekly' , last_sunday_body )
262+ if len (parts ) > 1 :
263+ open_to_work_content = "# #OpenToWork Weekly" + parts [1 ]
264+ print (" - Successfully extracted #OpenToWork Weekly section." )
265+ else :
266+ print (" - WARNING: Could not find '# OpenToWork Weekly' heading in last Sunday's email." )
167267 else :
168- print (" > Using 'description' field from API." , flush = True )
268+ print (" - WARNING: Could not find last Sunday's email." )
169269
170- description = description .replace ('"' , "'" )
171- final_title = raw_subject .replace ('"' , "'" )
172- permalink = f"/archive/{ slug } /"
173-
174- publish_date_obj = parse_date (latest_email .get ('publish_date' ))
175- formatted_date = publish_date_obj .strftime ('%Y-%m-%d %H:%M:%S.%f' )[:- 3 ] + '+00:00'
176-
177- processed_body = process_html_body (original_body )
270+ except requests .exceptions .RequestException as e :
271+ print (f" - ERROR fetching last Sunday's email: { e } " )
272+
273+ new_subject = f"🌶️ Hot Fudge Sunday for { today .strftime ('%Y-%m-%d' )} "
274+ new_body = f"""
275+ ## Last Week
276+
277+ A look at the week behind...
278+
279+ ## This Week
280+
281+ A look at the week ahead...
282+
283+ { digest_content }
284+ { open_to_work_content if open_to_work_content else '# #OpenToWork Weekly' }
285+ """
286+
287+ print (f"\n > Creating new digest email: '{ new_subject } '" )
288+
289+ payload = {
290+ "subject" : new_subject ,
291+ "body" : new_body .strip (),
292+ "status" : "draft"
293+ }
294+
295+ try :
296+ response = requests .post ("https://api.buttondown.email/v1/emails" , headers = {"Authorization" : f"Token { BUTTONDOWN_API_KEY } " , "Content-Type" : "application/json" }, json = payload )
178297
179- frontmatter = f"""---
180- title: "{ final_title } "
181- permalink: "{ permalink } "
182- description: "{ description } "
183- date: { formatted_date }
184- ---
185-
186- """
187- final_content = frontmatter + processed_body
188-
189- if SYNC_PATH :
190- output_dir = Path (SYNC_PATH ).expanduser ()
191- if output_dir .is_dir ():
192- output_file = output_dir / f"{ slug } .md"
193- try :
194- output_file .write_text (final_content , encoding = 'utf-8' )
195- print (f"\n Successfully saved file to: { output_file } " )
196- except Exception as e :
197- print (f"\n ERROR: Could not write file. { e } " )
198- else :
199- print (f"\n ERROR: SYNC_PATH '{ SYNC_PATH } ' is not a valid directory. Printing to screen instead." )
200- _print_content_to_screen (final_content )
298+ if response .status_code == 201 :
299+ print (" - SUCCESS: Sunday digest created successfully in Buttondown." )
201300 else :
202- print ("\n Warning: SYNC_PATH not set in .env file. Printing to screen." )
203- _print_content_to_screen (final_content )
204-
301+ print (f" - FAILED: API request failed with status code { response .status_code } " )
302+ print (f" Response: { response .text } " )
205303 except requests .exceptions .RequestException as e :
206- print (f"API request failed: { e } " )
207- except (KeyError , IndexError ):
208- print ("Could not find expected data in API response." )
209- except Exception as e :
210- print (f"An unexpected error occurred: { e } " )
304+ print (f" - FAILED: An error occurred during the API request: { e } " )
211305
212306
213307def main ():
@@ -216,12 +310,14 @@ def main():
216310
217311 while True :
218312 print ("\n What would you like to do?" )
219- print (" 1. Process new export (creates permalinks, keeps emoji in titles)" )
220- print (" 2. Retry failed descriptions in an 'emails_ready_for_import' folder" )
221- print (" 3. Fix empty alt tags & comments in an 'emails_ready_for_import' folder" )
222- print (" 4. Sync latest email and save to file (via API)" )
223- print (" 5. Exit" )
224- choice = input ("Enter your choice (1, 2, 3, 4, or 5): " )
313+ print (" 1. Process new export" )
314+ print (" 2. Retry failed descriptions" )
315+ print (" 3. Fix empty alt tags & comments" )
316+ print (" 4. Sync latest email and save to file" )
317+ print (" 5. Create skeleton email(s)" )
318+ print (" 6. Create Hot Fudge Sunday digest" )
319+ print (" 7. Exit" )
320+ choice = input ("Enter your choice: " )
225321
226322 if choice == '1' :
227323 process_new_export ()
@@ -236,6 +332,12 @@ def main():
236332 sync_latest_from_api ()
237333 break
238334 elif choice == '5' :
335+ create_daily_emails ()
336+ break
337+ elif choice == '6' :
338+ create_sunday_digest ()
339+ break
340+ elif choice == '7' :
239341 print ("Exiting." )
240342 break
241343 else :
0 commit comments