JustToThePoint English Website Version
JustToThePoint en español

Bible Verse of the Day in Python

Sometimes people don’t want to hear the truth because they don’t want their illusions destroyed, Friedrich Nietzsche.

Consistency is key when building a productive digital environment. For my daily workflow, I wanted the terminal greetings to be not just informative, but spiritually grounding. Hardcoding a quote every day is tedious, and relying solely on an internet connection can be fragile.

To solve this, I wrote bible_verse.py. This module acts as the religious/content engine for my greeting scripts, designed to be robust and flexible. It attempts to retrieve a verse through a cascading series of sources:

  1. Online APIs: It first tries to fetch a “Verse of the Day” from services like Our Daily Manna and YouVersion. This is great for variety when you are online.
  2. Local Reading Plan: If the network or the service API is down, it falls back to a local text file (daily_plan.txt). It uses the current day of the year to cycle through your personal reading list.
  3. Offline Lookup Engines: To display the text for the local plan, it queries two powerful offline tools: the freebible Python library (World English Bible) and the diatheke CLI tool (Sword Project, KJV). This ensures you get the actual scripture text without needing a live web connection.
  4. Fallback: Finally, if all else fails, it serves a hardcoded classic verse.

The script below is the implementation of this logic, featuring graceful dependency handling and colored terminal output.

image info

"""
bible_verse.py — Bible verse of the day for greetings.py
──────────────────────────────────────────────────────────
This module supplies a daily Bible verse for terminal greeting scripts.
It tries multiple sources in a cascading sequence, or optionally prints
all available sources.

Sources (in order of preference when `full=False`):

   1. YouVersion Bible API (online)
       — Verse of the day from YouVersion's community‑curated plan.
       — Requires an API key stored in password‑store (`pass apps/yvp_app_key`).

   2. Our Manna Daily Bread API (online)
       — Devotional verse from ourmanna.org.

   3. Personal daily plan (local file + offline/online lookup)
       — Reads ~/dotfiles/myArch/config/bible/daily_plan.txt
         (one Bible reference per line; supports ranges like "Psalm 23:1‑3").
       — Offline lookups are performed with:
           • `diatheke` (Sword project, KJV module engKJV2006eb)
           • `freebible` Python library (`bibles.web.quote`, KJV)
       — If both offline lookups fail, falls back to bible‑api.com public API.

    4. Hardcoded fallback verse (always available)
       — John 3:16 (KJV) when all other sources fail.

Integration:
    from bible_verse import verse_of_the_day
    verse_of_the_day()           # first available source
    verse_of_the_day(full=True)  # print all sources

Dependencies (all optional; the module degrades gracefully):
    pip install requests freebible colorama
    External tools:
      - diatheke (from the Sword project) must be on PATH.
      - password‑store (`pass`) for YouVersion API key (optional).

Optional colour support:
    pip install colorama
"""

import textwrap
import datetime
import html
from pathlib import Path
import re
import subprocess

# ----------------------------------------------------------------------
# Optional dependencies — we handle ImportError gracefully so the
# module can still be imported even if a particular library is missing.
# The actual functions fall back to other sources when unavailable.
# ----------------------------------------------------------------------
try:
    import requests
    HAS_REQUESTS = True
except ImportError:
    HAS_REQUESTS = False

# ----------------------------------------------------------------------
# freebible is imported early because it's used as a core offline source.
# If it's missing, the module will raise ImportError – consider wrapping
# the import in a try/except if you need the module to load without it.
# ----------------------------------------------------------------------
try:
    from freebible import bibles
    HAS_BIBLES = True
except ImportError:
    HAS_BIBLES = False
    bibles = None

try:
    from colorama import Fore, Style
    HAS_COLOR = True
except ImportError:
    # If colorama is missing, provide dummy classes so that formatting
    # codes are empty strings and nothing breaks.
    class Fore:  # type: ignore
        CYAN = YELLOW = GREEN = MAGENTA = RESET = ""
    class Style:  # type: ignore
        RESET_ALL = ""
    HAS_COLOR = False


# ── Configuration ─────────────────────────────────────────────────────────────

# Your personal reading plan — one reference per line
# Format examples:   Genesis 1:1     John 3:16     Psalm 23:1
DAILY_PLAN_FILE = Path.home() / "dotfiles/myArch/config/bible/daily_plan.txt"
"""Path to a plain-text file containing a personal Bible reading plan."""

# Fallback verse when nothing else works
FALLBACK_VERSE = {
    "ref":  "John 3:16",
    "text": "For God so loved the world, that he gave his only begotten Son, "
            "that whosoever believeth in him should not perish, but have everlasting life.",
    "source": "KJV (hardcoded fallback)",
}
"""Hardcoded dictionary providing a known verse when both network and offline database fail."""

# Width for text wrapping
WRAP_WIDTH = 72
"""Maximum line length when printing the verse text."""

# ----------------------------------------------------------------------
# Book name → Sword / freebible abbreviation mapping
# Keys are full book names as they might appear in a daily plan.
# Values are abbreviations used by diatheke and freebible.
# Extended to cover all canonical books.
# ----------------------------------------------------------------------
"""
>>> from freebible import bibles
# This is the internal book‑name vocabulary used by our installed version of freebible.
# It is not using full names like "1 Peter" or "Psalms" — instead it uses its own abbreviations,
# and these are the only valid keys when calling bibles.web.quote() or accessing the internal data.
>>> print(bibles.web.book_map.keys())
dict_keys(['Gen', 'Exo', 'Lev', 'Num', 'Deut', 'Josh', 'Judg', 'Rth', '1 Sam', '2 Sam', '1 Kgs', '2 Kgs', '1 Chron', '2 Chron', 'Ezra', 'Neh', 'Esth', 'Job', 'Pslm', 'Prov', 'Eccles', 'Song', 'Isa', 'Jer', 'Lam', 'Ezek', 'Dan', 'Hos', 'Joel', 'Amos', 'Obad', 'Jnh', 'Micah', 'Nah', 'Hab', 'Zeph', 'Haggai', 'Zech', 'Mal', 'Matt', 'Mrk', 'Luk', 'John', 'Acts', 'Rom', '1 Cor', '2 Cor', 'Gal', 'Ephes', 'Phil', 'Col', '1 Thess', '2 Thess', '1 Tim', '2 Tim', 'Titus', 'Philem', 'Hebrews', 'James', '1 Pet', '2 Pet', '1 John', '2 John', '3 John', 'Jude', 'Rev'])
"""

"""Mapping from full book names (as they appear in the plan) to Sword
module abbreviations.  The abbreviations are used by the `diatheke` tool
and the `bibles` Python library."""
BOOK_MAP = {
    # Pentateuch
    "Genesis": "Gen",
    "Exodus": "Exo",
    "Leviticus": "Lev",
    "Numbers": "Num",
    "Deuteronomy": "Deut",

    # History
    "Joshua": "Josh",
    "Judges": "Judg",
    "Ruth": "Rth",
    "1 Samuel": "1 Sam",
    "2 Samuel": "2 Sam",
    "1 Kings": "1 Kgs",
    "2 Kings": "2 Kgs",
    "1 Chronicles": "1 Chron",
    "2 Chronicles": "2 Chron",
    "Ezra": "Ezra",
    "Nehemiah": "Neh",
    "Esther": "Esth",

    # Wisdom
    "Job": "Job",
    "Psalms": "Pslm",
    "Proverbs": "Prov",
    "Ecclesiastes": "Eccles",
    "Song of Solomon": "Song",

    # Major Prophets
    "Isaiah": "Isa",
    "Jeremiah": "Jer",
    "Lamentations": "Lam",
    "Ezekiel": "Ezek",
    "Daniel": "Dan",

    # Minor Prophets
    "Hosea": "Hos",
    "Joel": "Joel",
    "Amos": "Amos",
    "Obadiah": "Obad",
    "Jonah": "Jnh",
    "Micah": "Micah",
    "Nahum": "Nah",
    "Habakkuk": "Hab",
    "Zephaniah": "Zeph",
    "Haggai": "Haggai",
    "Zechariah": "Zech",
    "Malachi": "Mal",

    # Gospels
    "Matthew": "Matt",
    "Mark": "Mrk",
    "Luke": "Luk",
    "John": "John",

    # Acts
    "Acts": "Acts",

    # Paul
    "Romans": "Rom",
    "1 Corinthians": "1 Cor",
    "2 Corinthians": "2 Cor",
    "Galatians": "Gal",
    "Ephesians": "Ephes",
    "Philippians": "Phil",
    "Colossians": "Col",
    "1 Thessalonians": "1 Thess",
    "2 Thessalonians": "2 Thess",
    "1 Timothy": "1 Tim",
    "2 Timothy": "2 Tim",
    "Titus": "Titus",
    "Philemon": "Philem",

    # General Epistles
    "Hebrews": "Hebrews",
    "James": "James",
    "1 Peter": "1 Pet",
    "2 Peter": "2 Pet",
    "1 John": "1 John",
    "2 John": "2 John",
    "3 John": "3 John",
    "Jude": "Jude",

    # Revelation
    "Revelation": "Rev",
}


# ── Helpers ───────────────────────────────────────────────────────────────────

def _print_verse(verse: dict | None) -> None:
    """
    Print a verse dictionary with colour and consistent formatting.

    Args:
        verse: Dictionary with keys 'ref', 'text', 'source'.
               If None, the function prints nothing and returns False.

    Returns:
        False when verse is None, otherwise prints and returns None (implicitly).
        This return convention is used by the caller to detect whether anything was printed.
    """
    if not verse:
        return False

    ref = verse.get("ref", "")
    text = verse.get("text", "")
    source = verse.get("source", "")

    print(f"\n{Fore.CYAN}📖  Bible Verse of the Day{Style.RESET_ALL}")
    print(f"    {Fore.YELLOW}{ref}{Style.RESET_ALL}")
    print()

    # Split on double newlines to respect verse paragraphing (e.g. from the reading plan)
    for para in text.split("\n\n"):
        # Wrap the verse text at WRAP_WIDTH, indented 4 spaces
        for line in textwrap.wrap(para, width=WRAP_WIDTH):
            print(f"    {line}")
        print()

    if source:
        # Use a slightly dimmed style for the attribution; we fall back
        # to ANSI escape code if colorama is not available.
        print(f"\n    {Fore.CYAN}{Style.RESET_ALL if not HAS_COLOR else ''}"
              f"    \033[2m— {source}\033[0m")
    print()


# ── Source 1: YouVersion & Our Manna ──────────────────────────────────────

# --- YouVersion API helpers ---

# echo "YOUVERSION-API-KEY" | pass insert -f apps/yvp_app_key
# Alternative 1: echo 'export YYOUVERSION-API-KEY="YOUR_APP_KEY_HERE"' >> ~/.zshrc
# Alternative 2: # .env file in your project (add to .gitignore!)
# YOUVERSION-API-KEY=your_actual_key_here
# >>> from dotenv import load_dotenv
# >>> load_dotenv()
# >>> app_key = os.getenv("YVP_APP_KEY")
def _get_youversion_headers() -> dict[str, str]:
    """
    Retrieve YouVersion API credentials from the system password store.

    Uses the `pass` command to read the key stored at `apps/yvp_app_key`.

    Returns:
        A dict suitable for the `headers` parameter of requests, e.g.
        {"YOUVERSION-API-KEY": "YOUR_APP_KEY_HERE"}
    """

    app_key = subprocess.check_output(
        ["pass", "apps/yvp_app_key"],
        text=True,
    ).strip()

    return {
        "X-YVP-App-Key": app_key,
    }

def _day_of_year() -> int:
    """
    Return the current day of year (1‑366).
    """

    return datetime.date.today().timetuple().tm_yday

def _youversion_passage_id(day: int) -> str | None:
    """
    Fetch the passage ID for a given day from YouVersion's verse_of_the_days endpoint.

    Args:
        day: Day number within the year (1‑366).

    Returns:
        A passage identifier like "JHN.3.16", or None on failure.
    """

    try:
        headers = _get_youversion_headers()
        url = f"https://api.youversion.com/v1/verse_of_the_days/{day}"
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        data = response.json()
        return data.get("passage_id")
    except Exception:
        return None

def _youversion_passage(
    passage_id: str,
    bible_id: int = 3034,
) -> dict | None:
    """
    Retrieve the actual text and reference for a YouVersion passage.

    Args:
        passage_id: Passage identifier, e.g. "JHN.3.16".
        bible_id:   Numeric ID of the Bible version (default 3034 = KJV).

    Returns:
        Dictionary with keys 'ref', 'text', 'source', or None on failure.
    """

    try:
        headers = _get_youversion_headers()

        url = f"https://api.youversion.com/v1/bibles/{bible_id}/passages/{passage_id}"
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        data = response.json()

        return {
            "ref": html.unescape(
                str(data.get("reference", "")).strip()
            ),

            "text": html.unescape(
                str(data.get("content", "")).strip()
            ),

            "source": "YouVersion",
        }

    except Exception:
        return None

def _from_youversion() -> dict | None:
    """
    Fetch today's Bible verse from YouVersion's official API.

    Returns:
        A dict with 'ref', 'text', 'source', or None if unavailable.
    """

    day = _day_of_year()
    passage_id = _youversion_passage_id(day)

    if not passage_id:
        return None

    return _youversion_passage(passage_id)

# --- Our Manna API ---

def _from_ourmanna_api() -> dict | None:
    """
    Fallback: Fetch the verse of the day from OurManna.com.

    curl --request GET \
     --url 'https://beta.ourmanna.com/api/v1/get?format=json&order=daily' \
     --header 'accept: application/json'

    API response
    {
        "verse": {
          "details": {
            "text": "To all perfection I see a limit, but your commands are boundless.",
            "reference": "Psalm 119:96",
            "version": "NIV",
            "verseurl": "http://www.ourmanna.com/"
          },
          "notice": "Powered by OurManna.com"
        }
    }

    Returns:
        Dictionary with keys 'ref', 'text', 'source', or None on error.
    """
    if not HAS_REQUESTS:
        return None

    url = "https://beta.ourmanna.com/api/v1/get?format=json&order=daily"

    try:
        resp = requests.get(url, timeout=10,
                            headers={"User-Agent": "greetings/1.0"})
        resp.raise_for_status()
        data = resp.json()

        details = data.get("verse", {}).get("details", {})
        text = html.unescape(str(details.get("text", "")).strip())
        ref  = html.unescape(str(details.get("reference", "")).strip())

        if not ref or not text:
            return None

        return {
            "ref":    ref,
            "text":   text,
            "source": "OurManna.com",
        }

    except Exception:
        return None

# ── Source 2: Daily plan + freebible KJV ──────────────────────────────────────

def _get_book_chapter_verses(
    reference: str,
) -> tuple[str, int, list[int]] | None:
    """
    Parse a Bible reference string into its components.

    Supports formats like:
        John 3:16
        1 Peter 5:6-7
        Psalm 23:1-3

    Args:
        reference: A string containing a Bible reference.

    Returns:
        A tuple (book_key, chapter, [verse_numbers]) where:
            - book_key is the Sword module abbreviation (from BOOK_MAP)
            - chapter is an integer
            - [verse_numbers] is a list of verse integers (single verse
              yields a one-element list, ranges are expanded).
        Returns None if the reference cannot be parsed.
    """
    # Attempt to match a reference of the form:
    #    :[-]
    # Captures:
    #   group 1 → book name (with optional leading digit, e.g. "1 Peter")
    #   group 2 → chapter number
    #   group 3 → starting verse
    #   group 4 → optional ending verse
    m = re.match(
        r"^(\d?\s?[A-Za-z]+(?:\s[A-Za-z]+)*)\s+(\d+):(\d+)(?:-(\d+))?$",
        reference.strip(), # remove surrounding whitespace before matching
        re.IGNORECASE, # allow case-insensitive book names
    )

    # If the pattern does not match, the reference is invalid
    if not m:
        return None

    # Extract the raw book name and normalize it to title case
    # (e.g. "1 peter" → "1 Peter")
    # The title() method returns a string where the first character in every word is upper case
    raw_book = m.group(1).strip().title() # normalise to title case

    # Look up the normalized book name in BOOK_MAP to obtain the
    # canonical abbreviation used by the Bible module.
    # (e.g., "1 Peter" → "1 Pet")
    book = BOOK_MAP.get(raw_book)
    if not book:
        # Unknown or unmapped book name
        return None

    # Convert the captured chapter number to an integer
    chapter = int(m.group(2))

    # Parse the starting verse
    start_verse = int(m.group(3))

    # Parse the ending verse if present; otherwise use the starting verse
    end_verse = int(m.group(4) or start_verse)

    # Expand the verse range into a list of integers
    # e.g. 6–7 → [6, 7], or 16 → [16]
    verses = list(range(start_verse, end_verse + 1))

    # Return the fully parsed reference components
    return book, chapter, verses

def _load_daily_plan(path: Path) -> list[str]:
    """
    Read the daily plan file and return a list of reference strings.

    Lines starting with # are treated as comments and skipped.
    Empty lines are skipped.

    Args:
        path: Path to the daily plan file.

    File format (daily_plan.txt):
        # Old Testament
        Genesis 1:1
        Genesis 1:2
        # New Testament
        John 3:16
        Romans 8:28

    Returns:
        A list of verse references (e.g. ["Genesis 1:1", "John 3:16"]).
    """
    # If the file does not exist, return an empty list
    if not path.exists():
        return []

    refs = [] # accumulator for valid reference lines

    # Read the file as UTF‑8 and iterate over each line
    for line in path.read_text(encoding="utf-8").splitlines():
        line = line.strip() # Read the file as UTF‑8 and iterate over each line

        # Skip empty lines and comment lines beginning with '#'
        if line and not line.startswith("#"):
            refs.append(line) # keep valid reference lines

    # Return the collected reference strings
    return refs

def _lookup_freebible(
    book: str,
    chapter: int,
    verse: int,
) -> str | None:
    """
    Look up a single verse using the `freebible` library's web API (KJV).

    This package provides a web‑based lookup (defaults to KJV).
    The `bibles.web.quote()` function takes a book abbreviation,
    chapter, and verse and returns a verse object whose string
    representation is the text.

    Args:
        book:    Sword module book abbreviation (e.g. "Gen").
        chapter: Chapter number.
        verse:   Verse number.

    Returns:
        The verse text as a plain string, or None on any error.
    """
    # If the bibles package is not available, abort immediately
    if not HAS_BIBLES:
        return None

    try:
        # Perform the lookup using the web-based API.
        # Returns a verse object whose __str__() yields the text.
        # >>> bibles.web.quote("John", 1, 1)
        # John 1:1 In the beginning was the Word, and the Word was with God, and the Word was God.
        verse_obj = bibles.web.quote(book, chapter, verse)

        # Convert to plain string and strip surrounding whitespace.
        return str(verse_obj).strip()

    except Exception:
        # Any failure (network, invalid reference, missing module)
        # results in a clean None return.
        return None

def _lookup_diatheke(
    book: str,
    chapter: int,
    verse: int,
) -> str | None:
    """
    Look up a single verse using the `diatheke` command-line tool.

    diatheke is part of the Sword project.  The module used here is
    engKJV2006eb (an electronic edition of the KJV).

    Args:
        book:    Sword module book abbreviation (e.g. "Gen").
        chapter: Chapter number.
        verse:   Verse number.

    Returns:
        The cleaned verse text (plain ASCII, no markup), or None if the
        lookup fails (diatheke not found, invalid reference, empty output).
    """
    # Construct a diatheke-style reference string, e.g. "Gen 1:1"
    ref = f"{book} {chapter}:{verse}"

    try:
        # Invoke diatheke with:
        #   -b engKJV2006eb   → select the KJV module
        #   -k           → lookup key
        # stderr suppressed to avoid noise on missing modules, etc.
        out = subprocess.check_output(
            ["diatheke", "-b", "engKJV2006eb", "-k", ref],
            stderr=subprocess.DEVNULL,
            text=True, # return output as str instead of bytes
        ).strip()

        # If diatheke produced no output, treat as failure
        if not out:
            return None

        # diatheke output typically starts with "ModuleName: verse text", "engKJV2006eb: In the beginning..."
        # We extract everything after the colon.
        first_line = out.splitlines()[0].strip()

        # Extract everything after the first colon.
        # Pattern: : 
        m = re.match(r"^[^:]+:\s*(.*)$", first_line)
        if not m:
            return None

        # Raw verse text (may still contain Sword markup)
        text = m.group(1).strip()

        # Remove any remaining Sword markup ( tags, etc.)
        # Remove  word-level tags
        text = re.sub(r"]*>", "", text)
        text = text.replace("", "")

        # Remove any remaining XML/HTML-like tags
        text = re.sub(r"<[^>]+>", "", text)

        # Collapse all whitespace sequences to single spaces
        return " ".join(text.split())

    except Exception:
        # Covers: diatheke not installed, bad exit code, invalid reference,
        # Unicode issues, etc.
        return None

def _lookup_bible_api(ref: str) -> str | None:
    """
    Look up a Bible reference using the public bible‑api.com endpoint.

    This is used as a fallback when both offline sources (diatheke,
    freebible) fail for a reading plan entry.

    Args:
        ref: A human‑readable reference string, e.g. "John 3:16".

    Returns:
        The full text of the passage, or None on any error.
    """
    try:
        r = requests.get(
            f"https://bible-api.com/{ref.replace(' ', '%20')}",
            timeout=10,
        )
        r.raise_for_status()

        data = r.json()

        return data.get("text", "").strip()

    except Exception:
        return None

def _from_daily_plan() -> dict | None:
    """
    Pick today's verse from the daily plan using day-of-year cycling.

    The plan file (one reference per line) is read; today's entry is
    determined by `day_of_year % len(plan)`.  Both diatheke and the
    `bibles` library are queried for each verse (to handle ranges),
    and their results are concatenated side‑by‑side for display.

    Returns:
        A dict with keys:
            - 'ref'   : the original reference string (e.g. "Psalm 23:1-3")
            - 'text'  : a combined string showing diatheke and freebible output
            - 'source': a description of the source
        Returns None if the plan file is empty, the reference cannot be
        parsed, or both lookups fail entirely.
    """
    refs = _load_daily_plan(DAILY_PLAN_FILE)
    if not refs:
        return None

    # day-of-year is 1‑based; we make it 0‑based for indexing
    day_of_year = datetime.date.today().timetuple().tm_yday - 1
    idx = day_of_year % len(refs)
    ref = refs[idx]

    parsed = _get_book_chapter_verses(ref)
    if not parsed:
        return None

    book, chapter, verses = parsed

    diatheke_texts = []
    freebible_texts = []

    for verse in verses:
        t1 = _lookup_diatheke(book, chapter, verse)
        t2 = _lookup_freebible(book, chapter, verse)

        if t1:
            diatheke_texts.append(t1)
        if t2:
            freebible_texts.append(t2)

    # Offline sources failed → attempt bible‑api.com
    fallback_text = None
    if not diatheke_texts and not freebible_texts:
        fallback_text = _lookup_bible_api(ref)

    # If everything failed, give up
    if not diatheke_texts and not freebible_texts and not fallback_text:
        return None

    # Build a combined text block showing each source separately
    return {
        "ref": ref,
        "text":
            (
                "Diatheke:\n" + " ".join(diatheke_texts) + "\n\n"
                if diatheke_texts else ""
            )
            + (
                "FreeBible:\n" + " ".join(freebible_texts) + "\n\n"
                if freebible_texts else ""
            )
            + (
                "Bible API:\n" + fallback_text
                if fallback_text else ""
            ),
        "source":
            f"KJV — daily plan day {idx + 1}/{len(refs)}; "
            f"Sources: Diatheke + FreeBible + BibleAPI",
    }

# ── Public entry point ────────────────────────────────────────────────────────

def verse_of_the_day(full: bool = False) -> None:
    """
    Pick and print a Bible verse of the day using the available sources.

    By default (`full=False`), the function tries sources in the following
    order and stops at the first successful one:
        1. YouVersion
        2. Our Manna
        3. Personal daily plan (with offline + online fallback)
    If none work, a hardcoded fallback verse is printed.

    When `full=True`, all available sources are printed one after another.

    This function is designed to be called from a greeting script; it handles
    all I/O and error recovery internally.
    """
    sources = [
        _from_youversion,
        _from_ourmanna_api,
        _from_daily_plan,
    ]

    if not full:
        for fn in sources:
            if _print_verse(fn()):
                return

        # ultimate fallback
        _print_verse(FALLBACK_VERSE)
        return

    # FULL MODE → print everything available
    printed_any = False
    for fn in sources:
        if _print_verse(fn()):
            printed_any = True

    if not printed_any:
        _print_verse(FALLBACK_VERSE)

def check_setup() -> None:
    """
    Print a diagnostic about what is installed and configured.

    Run this when setting up for the first time to verify what is installed
    and whether the daily plan file is present and correctly formatted.
    """
    print("Bible verse setup check:")
    print(f"  requests:   {'✅ installed' if HAS_REQUESTS  else '❌ missing — pip install requests'}")
    print(f"  freebible:  {'✅ installed' if HAS_FREEBIBLE else '❌ missing — pip install freebible'}")
    print(f"  colorama:   {'✅ installed' if HAS_COLOR     else '⚠️  missing — pip install colorama (optional)'}")
    print(f"  daily plan: {'✅ found' if DAILY_PLAN_FILE.exists() else f'⚠️  not found — create {DAILY_PLAN_FILE}'}")

    if DAILY_PLAN_FILE.exists():
        refs = _load_daily_plan(DAILY_PLAN_FILE)
        print(f"             {len(refs)} references loaded")
        day = datetime.date.today().timetuple().tm_yday - 1
        today_ref = refs[day % len(refs)] if refs else "—"
        print(f"             Today's reference: {today_ref}")

# ── Script entry point ────────────────────────────────────────────────────────
if __name__ == "__main__":
    import sys
    if "--check" in sys.argv:
        check_setup()
    else:
        verse_of_the_day()
Bitcoin donation

JustToThePoint Copyright © 2011 - 2026 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun. Social Issues, Join us.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.