Files
saints/get_saint_of_the_day.py
T
2026-01-24 01:18:42 -05:00

225 lines
6.3 KiB
Python

#!/usr/bin/env python3
"""
Script to get the Wikipedia page for the saint of the day based on French saints days calendar.
Optionally posts to Mastodon with the --post flag.
"""
import argparse
import csv
import os
from datetime import datetime
from pathlib import Path
import urllib.parse
from dotenv import load_dotenv
from mastodon import Mastodon
# Entries that are holidays, feasts, or events rather than actual saints/people
NON_PERSON_ENTRIES = {
"Epiphany",
"Holy Name of Jesus",
"Conversion of St. Paul",
"Presentation of the Lord",
"Our Lady of Lourdes",
"Chair of St. Peter",
"Annunciation",
"Our Lady of Fatima",
"Visitation of the Virgin Mary",
"Nativity of John the Baptist",
"First Martyrs of Rome",
"Our Lady of Mount Carmel",
"Dedication of St. Mary Major",
"Transfiguration",
"Assumption of Mary",
"Queenship of Mary",
"Beheading of John the Baptist",
"Nativity of Mary",
"Holy Name of Mary",
"Exaltation of the Holy Cross",
"Our Lady of Sorrows",
"Guardian Angels",
"Our Lady of the Rosary",
"All Saints",
"All Souls",
"Dedication of the Lateran Basilica",
"Dedication of the Basilicas of Peter and Paul",
"Presentation of Mary",
"Immaculate Conception",
"Our Lady of Guadalupe",
"Holy Innocents",
}
def get_standardized_name(saint_name: str) -> str:
"""Get the standardized name used for Wikipedia URLs and filenames."""
return saint_name.replace(" ", "_")
def get_wikipedia_url(saint_name: str) -> str:
"""Generate Wikipedia URL for a saint name."""
wiki_name = get_standardized_name(saint_name)
encoded_name = urllib.parse.quote(wiki_name, safe="_")
return f"https://en.wikipedia.org/wiki/{encoded_name}"
def get_saint_picture_path(saint_name: str) -> Path | None:
"""Find the picture file for a saint, if it exists."""
pics_dir = Path(__file__).parent / "pics"
standardized_name = get_standardized_name(saint_name)
# Check for common image extensions
for ext in [".jpg", ".jpeg", ".png", ".gif", ".webp"]:
pic_path = pics_dir / f"{standardized_name}{ext}"
if pic_path.exists():
return pic_path
return None
def get_saints_for_today() -> list[dict]:
"""
Read the CSV and return saints matching today's date.
Returns a list of dicts with saint info and Wikipedia URLs.
"""
today = datetime.now()
today_str = today.strftime("%B %-d") # e.g., "January 22"
csv_path = Path(__file__).parent / "saints_days_france.csv"
saints = []
with open(csv_path, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
if row["Date"] == today_str:
saint_name = row["Saint Name"]
# Skip non-person entries (holidays, feasts, events)
if saint_name in NON_PERSON_ENTRIES:
continue
saints.append(
{
"name": saint_name,
"french_name": row["French Name"],
"notes": row["Notes"],
"wikipedia_url": get_wikipedia_url(saint_name),
"picture_path": get_saint_picture_path(saint_name),
}
)
return saints
def format_mastodon_post(saint: dict) -> str:
"""Format a saint's information as a Mastodon post."""
today = datetime.now()
date_str = today.strftime("%B %d")
lines = [
f"{date_str}: the Feast of Saint {saint['name']}",
]
lines.extend(
[
saint["wikipedia_url"],
"",
"#saints #hagiography",
]
)
return "\n".join(lines)
def post_to_mastodon(saint: dict) -> dict:
"""Post a saint to Mastodon. Returns the status dict from the API."""
# Load environment variables
load_dotenv(Path(__file__).parent / ".env")
instance_url = os.getenv("MASTODON_INSTANCE_URL")
access_token = os.getenv("MASTODON_ACCESS_TOKEN")
if not instance_url or not access_token:
raise ValueError(
"Missing Mastodon credentials. Please set MASTODON_INSTANCE_URL and "
"MASTODON_ACCESS_TOKEN in .env file."
)
if access_token == "your_access_token_here":
raise ValueError(
"Please update the MASTODON_ACCESS_TOKEN in .env with your actual token."
)
# Initialize Mastodon client
mastodon = Mastodon(
access_token=access_token,
api_base_url=instance_url,
)
# Format the post
post_text = format_mastodon_post(saint)
# Upload image if available
media_ids = None
if saint["picture_path"]:
print(f" Uploading image: {saint['picture_path'].name}")
media = mastodon.media_post(
str(saint["picture_path"]),
description=f"Image of {saint['name']}",
)
media_ids = [media["id"]]
# Post the status
status = mastodon.status_post(
post_text,
media_ids=media_ids,
visibility="public",
)
return status
def main():
parser = argparse.ArgumentParser(
description="Get the saint of the day and optionally post to Mastodon."
)
parser.add_argument(
"--post",
action="store_true",
help="Post to Mastodon (requires credentials in .env)",
)
args = parser.parse_args()
saints = get_saints_for_today()
if not saints:
print("No saint's day today (or today is a holiday/feast day).")
return
for saint in saints:
print(f"Saint: {saint['name']}")
print(f"French: {saint['french_name']}")
if saint["notes"]:
print(f"Notes: {saint['notes']}")
print(f"Wikipedia: {saint['wikipedia_url']}")
if saint["picture_path"]:
print(f"Picture: {saint['picture_path']}")
else:
print("Picture: (none available)")
if args.post:
if not saint["picture_path"]:
print("\nSkipping Mastodon post (no picture available)")
else:
print("\nPosting to Mastodon...")
try:
status = post_to_mastodon(saint)
print(f" Posted! URL: {status['url']}")
except Exception as e:
print(f" Error posting: {e}")
print()
if __name__ == "__main__":
main()