initial commit
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
#!/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['french_name']}",
|
||||
f"({saint['name']})",
|
||||
]
|
||||
|
||||
if saint["notes"]:
|
||||
lines.append(f"{saint['notes']}")
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
saint["wikipedia_url"],
|
||||
"",
|
||||
"#SaintOfTheDay #Saints #Catholic #France",
|
||||
])
|
||||
|
||||
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()
|
||||
Reference in New Issue
Block a user