initial commit
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
|||||||
|
# Python-generated files
|
||||||
|
__pycache__/
|
||||||
|
*.py[oc]
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
wheels/
|
||||||
|
*.egg-info
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv
|
||||||
|
|
||||||
|
# env
|
||||||
|
.env
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.13
|
||||||
@@ -0,0 +1,336 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Script to download pictures of saints from Wikipedia.
|
||||||
|
Run this once to populate the pics/ directory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import requests
|
||||||
|
import urllib.parse
|
||||||
|
from pathlib import Path
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
|
# 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",
|
||||||
|
}
|
||||||
|
|
||||||
|
WIKIPEDIA_API = "https://en.wikipedia.org/w/api.php"
|
||||||
|
|
||||||
|
# Wikimedia requires a descriptive User-Agent per their policy
|
||||||
|
# https://meta.wikimedia.org/wiki/User-Agent_policy
|
||||||
|
USER_AGENT = "SaintsPictureDownloader/1.0 (Educational project for French saints calendar)"
|
||||||
|
|
||||||
|
# Mapping of CSV saint names to alternate Wikipedia article titles to try
|
||||||
|
# Many saints have disambiguation issues or different article naming conventions
|
||||||
|
ALTERNATE_TITLES = {
|
||||||
|
"Agatha": ["Agatha of Sicily"],
|
||||||
|
"Agnes": ["Agnes of Rome"],
|
||||||
|
"Albert the Great": ["Albertus Magnus"],
|
||||||
|
"Andrew Dung-Lac and Companions": ["Andrew Dung-Lac"],
|
||||||
|
"Andrew Kim Taegon and Companions": ["Andrew Kim Taegon"],
|
||||||
|
"Anselm": ["Anselm of Canterbury"],
|
||||||
|
"Anthony of Egypt": ["Anthony the Great"],
|
||||||
|
"Apollinaris": ["Apollinaris of Ravenna"],
|
||||||
|
"Athanasius": ["Athanasius of Alexandria"],
|
||||||
|
"Augustine": ["Augustine of Hippo"],
|
||||||
|
"Augustine Zhao Rong and Companions": ["Augustine Zhao Rong"],
|
||||||
|
"Bartholomew": ["Bartholomew the Apostle"],
|
||||||
|
"Basil the Great and Gregory Nazianzen": ["Basil of Caesarea", "Gregory of Nazianzus"],
|
||||||
|
"Bede the Venerable": ["Bede"],
|
||||||
|
"Benedict": ["Benedict of Nursia"],
|
||||||
|
"Bernardine of Siena": ["Bernardino of Siena"],
|
||||||
|
"Blaise": ["Saint Blaise"],
|
||||||
|
"Boniface": ["Saint Boniface"],
|
||||||
|
"Bruno": ["Bruno of Cologne"],
|
||||||
|
"Cajetan": ["Saint Cajetan", "Gaetano dei Conti di Thiene"],
|
||||||
|
"Callistus I": ["Pope Callixtus I"],
|
||||||
|
"Casimir": ["Casimir Jagiellon", "Saint Casimir"],
|
||||||
|
"Charles Lwanga and Companions": ["Charles Lwanga"],
|
||||||
|
"Christopher Magallanes and Companions": ["Cristóbal Magallanes Jara"],
|
||||||
|
"Clare": ["Clare of Assisi"],
|
||||||
|
"Clement I": ["Pope Clement I"],
|
||||||
|
"Columban": ["Columbanus"],
|
||||||
|
"Cornelius and Cyprian": ["Pope Cornelius", "Cyprian"],
|
||||||
|
"Damasus I": ["Pope Damasus I"],
|
||||||
|
"Denis and Companions": ["Denis of Paris", "Saint Denis"],
|
||||||
|
"Ephrem": ["Ephrem the Syrian"],
|
||||||
|
"Fabian": ["Pope Fabian"],
|
||||||
|
"George": ["Saint George"],
|
||||||
|
"Gertrude": ["Gertrude the Great"],
|
||||||
|
"Gregory VII": ["Pope Gregory VII"],
|
||||||
|
"Gregory the Great": ["Pope Gregory I"],
|
||||||
|
"Hedwig": ["Hedwig of Silesia"],
|
||||||
|
"Henry": ["Henry II, Holy Roman Emperor"],
|
||||||
|
"Isaac Jogues and John de Brebeuf": ["Isaac Jogues", "Jean de Brébeuf"],
|
||||||
|
"Isidore the Farmer": ["Isidore the Laborer"],
|
||||||
|
"James": ["James the Great", "James, son of Zebedee"],
|
||||||
|
"Jerome Emiliani": ["Jerome Emiliani"],
|
||||||
|
"Joachim and Anne": ["Saint Joachim", "Saint Anne"],
|
||||||
|
"John": ["John the Apostle"],
|
||||||
|
"John Baptist de la Salle": ["Jean-Baptiste de La Salle"],
|
||||||
|
"John Damascene": ["John of Damascus"],
|
||||||
|
"John Fisher and Thomas More": ["John Fisher", "Thomas More"],
|
||||||
|
"John I": ["Pope John I"],
|
||||||
|
"John Paul II": ["Pope John Paul II"],
|
||||||
|
"John XXIII": ["Pope John XXIII"],
|
||||||
|
"John of Kanty": ["John Cantius"],
|
||||||
|
"Josaphat": ["Josaphat Kuntsevych"],
|
||||||
|
"Joseph the Worker": ["Saint Joseph"],
|
||||||
|
"Justin": ["Justin Martyr"],
|
||||||
|
"Lawrence": ["Saint Lawrence", "Lawrence of Rome"],
|
||||||
|
"Lawrence Ruiz and Companions": ["Lorenzo Ruiz", "Lawrence Ruiz"],
|
||||||
|
"Leo the Great": ["Pope Leo I"],
|
||||||
|
"Louis IX": ["Louis IX of France"],
|
||||||
|
"Luke": ["Luke the Evangelist"],
|
||||||
|
"Margaret of Scotland": ["Saint Margaret of Scotland"],
|
||||||
|
"Mark": ["Mark the Evangelist"],
|
||||||
|
"Martin I": ["Pope Martin I"],
|
||||||
|
"Mary Magdalene de Pazzi": ["Mary Magdalene de' Pazzi"],
|
||||||
|
"Mary Mother of God": ["Mary, mother of Jesus"],
|
||||||
|
"Mary and Martha": ["Mary of Bethany", "Martha of Bethany"],
|
||||||
|
"Matthew": ["Matthew the Apostle"],
|
||||||
|
"Matthias": ["Matthias the Apostle"],
|
||||||
|
"Michael": ["Michael (archangel)"],
|
||||||
|
"Monica": ["Saint Monica"],
|
||||||
|
"Pancras": ["Pancras of Rome"],
|
||||||
|
"Patrick": ["Saint Patrick"],
|
||||||
|
"Paul Miki and Companions": ["Paul Miki"],
|
||||||
|
"Peter and Paul": ["Saint Peter", "Paul the Apostle"],
|
||||||
|
"Philip and James": ["Philip the Apostle", "James, son of Alphaeus"],
|
||||||
|
"Pius V": ["Pope Pius V"],
|
||||||
|
"Pius X": ["Pope Pius X"],
|
||||||
|
"Pius of Pietrelcina": ["Padre Pio"],
|
||||||
|
"Pontian and Hippolytus": ["Pope Pontian", "Hippolytus of Rome"],
|
||||||
|
"Raymond of Penyafort": ["Raymond of Peñafort"],
|
||||||
|
"Sebastian": ["Saint Sebastian"],
|
||||||
|
"Seven Founders of the Servite Order": ["Seven Holy Founders of the Servite Order"],
|
||||||
|
"Sharbel Makhluf": ["Charbel Makhluf"],
|
||||||
|
"Simon and Jude": ["Simon the Zealot", "Jude the Apostle"],
|
||||||
|
"Sixtus II and Companions": ["Pope Sixtus II"],
|
||||||
|
"Stanislaus": ["Stanislaus of Szczepanów"],
|
||||||
|
"Stephen of Hungary": ["Stephen I of Hungary"],
|
||||||
|
"Sylvester I": ["Pope Sylvester I"],
|
||||||
|
"Teresa Benedicta of the Cross": ["Edith Stein"],
|
||||||
|
"Teresa of Avila": ["Teresa of Ávila"],
|
||||||
|
"Therese of the Child Jesus": ["Thérèse of Lisieux"],
|
||||||
|
"Thomas": ["Thomas the Apostle"],
|
||||||
|
"Timothy and Titus": ["Saint Timothy", "Saint Titus"],
|
||||||
|
"Valentine": ["Saint Valentine"],
|
||||||
|
"Wenceslaus": ["Wenceslaus I, Duke of Bohemia"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_session() -> requests.Session:
|
||||||
|
"""Create a requests session with proper headers for Wikimedia."""
|
||||||
|
session = requests.Session()
|
||||||
|
session.headers.update({
|
||||||
|
"User-Agent": USER_AGENT,
|
||||||
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
|
"Accept-Encoding": "gzip, deflate",
|
||||||
|
"Connection": "keep-alive",
|
||||||
|
})
|
||||||
|
return session
|
||||||
|
|
||||||
|
|
||||||
|
def get_standardized_name(saint_name: str) -> str:
|
||||||
|
"""Get the standardized name used for Wikipedia URLs and filenames."""
|
||||||
|
return saint_name.replace(" ", "_")
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_saints() -> list[str]:
|
||||||
|
"""Read the CSV and return all unique saint names (excluding non-person entries)."""
|
||||||
|
csv_path = Path(__file__).parent / "saints_days_france.csv"
|
||||||
|
saints = set()
|
||||||
|
|
||||||
|
with open(csv_path, "r", encoding="utf-8") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
for row in reader:
|
||||||
|
saint_name = row["Saint Name"]
|
||||||
|
if saint_name not in NON_PERSON_ENTRIES:
|
||||||
|
saints.add(saint_name)
|
||||||
|
|
||||||
|
return sorted(saints)
|
||||||
|
|
||||||
|
|
||||||
|
def get_wikipedia_image_url(session: requests.Session, page_title: str) -> str | None:
|
||||||
|
"""
|
||||||
|
Use Wikipedia API to get the main image URL for a page.
|
||||||
|
Uses imageinfo prop to get the direct file URL.
|
||||||
|
Returns the image URL or None if no image found.
|
||||||
|
"""
|
||||||
|
# First, get the page image filename
|
||||||
|
params = {
|
||||||
|
"action": "query",
|
||||||
|
"titles": page_title,
|
||||||
|
"prop": "pageimages",
|
||||||
|
"piprop": "name", # Get the filename instead of thumbnail URL
|
||||||
|
"format": "json",
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = session.get(WIKIPEDIA_API, params=params, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
pages = data.get("query", {}).get("pages", {})
|
||||||
|
image_filename = None
|
||||||
|
for page_id, page_data in pages.items():
|
||||||
|
if page_id == "-1":
|
||||||
|
return None
|
||||||
|
image_filename = page_data.get("pageimage")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not image_filename:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Now get the actual image URL using imageinfo
|
||||||
|
params = {
|
||||||
|
"action": "query",
|
||||||
|
"titles": f"File:{image_filename}",
|
||||||
|
"prop": "imageinfo",
|
||||||
|
"iiprop": "url",
|
||||||
|
"iiurlwidth": 500, # Request a 500px wide thumbnail
|
||||||
|
"format": "json",
|
||||||
|
}
|
||||||
|
|
||||||
|
response = session.get(WIKIPEDIA_API, params=params, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
pages = data.get("query", {}).get("pages", {})
|
||||||
|
for page_id, page_data in pages.items():
|
||||||
|
# Files on Commons may show as "missing" (-1) from enwiki but still have imageinfo
|
||||||
|
imageinfo_list = page_data.get("imageinfo", [])
|
||||||
|
if imageinfo_list:
|
||||||
|
imageinfo = imageinfo_list[0]
|
||||||
|
# Prefer thumburl (resized), fall back to original url
|
||||||
|
return imageinfo.get("thumburl") or imageinfo.get("url")
|
||||||
|
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print(f" Error fetching Wikipedia API: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def download_image(session: requests.Session, url: str, filepath: Path) -> bool:
|
||||||
|
"""Download an image from URL and save to filepath."""
|
||||||
|
try:
|
||||||
|
# Add image-specific accept header for this request
|
||||||
|
headers = {"Accept": "image/webp,image/apng,image/*,*/*;q=0.8"}
|
||||||
|
response = session.get(url, headers=headers, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
with open(filepath, "wb") as f:
|
||||||
|
f.write(response.content)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print(f" Error downloading image: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
pics_dir = Path(__file__).parent / "pics"
|
||||||
|
pics_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
saints = get_all_saints()
|
||||||
|
print(f"Found {len(saints)} saints to process.\n")
|
||||||
|
|
||||||
|
# Create a persistent session
|
||||||
|
session = create_session()
|
||||||
|
|
||||||
|
downloaded = 0
|
||||||
|
skipped = 0
|
||||||
|
failed = 0
|
||||||
|
|
||||||
|
for saint_name in saints:
|
||||||
|
standardized_name = get_standardized_name(saint_name)
|
||||||
|
|
||||||
|
# Check if we already have an image for this saint
|
||||||
|
existing_files = list(pics_dir.glob(f"{standardized_name}.*"))
|
||||||
|
if existing_files:
|
||||||
|
print(f"[SKIP] {saint_name} - already have {existing_files[0].name}")
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"[FETCH] {saint_name}...")
|
||||||
|
|
||||||
|
# Get image URL from Wikipedia - try primary name first, then alternates
|
||||||
|
image_url = get_wikipedia_image_url(session, saint_name)
|
||||||
|
|
||||||
|
if not image_url and saint_name in ALTERNATE_TITLES:
|
||||||
|
for alt_title in ALTERNATE_TITLES[saint_name]:
|
||||||
|
print(f" Trying alternate: {alt_title}")
|
||||||
|
image_url = get_wikipedia_image_url(session, alt_title)
|
||||||
|
if image_url:
|
||||||
|
break
|
||||||
|
time.sleep(0.3) # Small delay between alternate attempts
|
||||||
|
|
||||||
|
if not image_url:
|
||||||
|
print(f" No image found on Wikipedia")
|
||||||
|
failed += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Determine file extension from URL
|
||||||
|
url_path = urllib.parse.urlparse(image_url).path
|
||||||
|
ext = Path(url_path).suffix.lower()
|
||||||
|
# Handle URLs with size suffix like .jpg/500px-foo.jpg
|
||||||
|
if ext not in [".jpg", ".jpeg", ".png", ".gif", ".svg", ".webp"]:
|
||||||
|
# Try to extract from the path
|
||||||
|
for known_ext in [".jpg", ".jpeg", ".png", ".gif", ".svg", ".webp"]:
|
||||||
|
if known_ext in url_path.lower():
|
||||||
|
ext = known_ext
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
ext = ".jpg" # Default fallback
|
||||||
|
|
||||||
|
filepath = pics_dir / f"{standardized_name}{ext}"
|
||||||
|
|
||||||
|
if download_image(session, image_url, filepath):
|
||||||
|
print(f" Saved to {filepath.name}")
|
||||||
|
downloaded += 1
|
||||||
|
else:
|
||||||
|
failed += 1
|
||||||
|
|
||||||
|
# Be nice to Wikipedia's servers - random delay between 0.5 and 1.5 seconds
|
||||||
|
time.sleep(0.5 + random.random())
|
||||||
|
|
||||||
|
print(f"\nDone! Downloaded: {downloaded}, Skipped: {skipped}, Failed: {failed}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -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()
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[project]
|
||||||
|
name = "saints"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = [
|
||||||
|
"mastodon-py>=2.1.4",
|
||||||
|
"python-dotenv>=1.2.1",
|
||||||
|
"requests>=2.32.5",
|
||||||
|
]
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blurhash"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/24/f3/9e636182d0e6b3f6b7879242f7f8add78238a159e8087ec39941f5d65af7/blurhash-1.1.5.tar.gz", hash = "sha256:181e1484b6a8ab5cff0ef37739150c566f4a72f2ab0dcb79660b6cee69c137a9", size = 50859, upload-time = "2025-08-17T10:36:12.519Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bd/dc/cadbf64b335a2ee0f31a84d05f34551c2199caa6f639a90c9157b564d0d6/blurhash-1.1.5-py2.py3-none-any.whl", hash = "sha256:96a8686e8b9fced1676550b814e59256214e2d4033202b16c91271ed4d317fec", size = 6632, upload-time = "2025-08-17T10:36:11.404Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "certifi"
|
||||||
|
version = "2026.1.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "charset-normalizer"
|
||||||
|
version = "3.4.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "decorator"
|
||||||
|
version = "5.2.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "3.11"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mastodon-py"
|
||||||
|
version = "2.1.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "blurhash" },
|
||||||
|
{ name = "decorator" },
|
||||||
|
{ name = "python-dateutil" },
|
||||||
|
{ name = "python-magic", marker = "sys_platform != 'win32'" },
|
||||||
|
{ name = "python-magic-bin", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "requests" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/88/ec/1eccba4dda197e6993dd1b8a4fa5728f8ed64d3ba54d61ebfe2420a20f4e/mastodon_py-2.1.4.tar.gz", hash = "sha256:6602e9ca4db37c70b5adae5964d02e9a529f6cc8473947a314261008add208a5", size = 11636752, upload-time = "2025-09-23T09:39:04.156Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/02/eb/23afadb9a0aee04a52adfc010384da267b42b66be6cbb3ed2d3c3edc20f4/mastodon_py-2.1.4-py3-none-any.whl", hash = "sha256:447ce341cf9a67e70789abf6a2c1a54b52cd2cd021818ccb32c52f34804c7896", size = 123469, upload-time = "2025-09-23T09:39:02.515Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dateutil"
|
||||||
|
version = "2.9.0.post0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "six" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dotenv"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-magic"
|
||||||
|
version = "0.4.27"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/da/db/0b3e28ac047452d079d375ec6798bf76a036a08182dbb39ed38116a49130/python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b", size = 14677, upload-time = "2022-06-07T20:16:59.508Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/73/9f872cb81fc5c3bb48f7227872c28975f998f3e7c2b1c16e95e6432bbb90/python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3", size = 13840, upload-time = "2022-06-07T20:16:57.763Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-magic-bin"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/5d/10b9ac745d9fd2f7151a2ab901e6bb6983dbd70e87c71111f54859d1ca2e/python_magic_bin-0.4.14-py2.py3-none-win32.whl", hash = "sha256:34a788c03adde7608028203e2dbb208f1f62225ad91518787ae26d603ae68892", size = 397784, upload-time = "2017-10-02T16:30:15.806Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/c2/094e3d62b906d952537196603a23aec4bcd7c6126bf80eb14e6f9f4be3a2/python_magic_bin-0.4.14-py2.py3-none-win_amd64.whl", hash = "sha256:90be6206ad31071a36065a2fc169c5afb5e0355cbe6030e87641c6c62edc2b69", size = 409299, upload-time = "2017-10-02T16:30:18.545Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "requests"
|
||||||
|
version = "2.32.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "certifi" },
|
||||||
|
{ name = "charset-normalizer" },
|
||||||
|
{ name = "idna" },
|
||||||
|
{ name = "urllib3" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "saints"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "mastodon-py" },
|
||||||
|
{ name = "python-dotenv" },
|
||||||
|
{ name = "requests" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "mastodon-py", specifier = ">=2.1.4" },
|
||||||
|
{ name = "python-dotenv", specifier = ">=1.2.1" },
|
||||||
|
{ name = "requests", specifier = ">=2.32.5" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urllib3"
|
||||||
|
version = "2.6.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user