223 lines
6.3 KiB
Python
223 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"Saint of the Day - {date_str}",
|
|
"",
|
|
f"{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()
|