source code

Here is the source code for all generators utilized for the daily messages from Ethel Cain. Written in python and requires lyricsgenius and a Genius API token

import os
import lyricsgenius
import random
import re
import sys

# ---------------------------------------------------------
# GENIUS TOKEN (set it here)
# ---------------------------------------------------------
GENIUS_ACCESS_TOKEN = "ENTER_TOKEN_HERE"

# Cache file
CACHE_FILE = "ethel_cain_lyrics.txt"

# ---------------------------------------------------------
# Initialize Genius API client
# ---------------------------------------------------------
genius = lyricsgenius.Genius(
    GENIUS_ACCESS_TOKEN,
    remove_section_headers=True,
    skip_non_songs=True,
    excluded_terms=["(Remix)", "(Live)"],
    timeout=15
)

# Global storage
all_lyrics = ""
words = []
markov = {}

# ---------------------------------------------------------
# Save lyrics to cache
# ---------------------------------------------------------
def save_cache(text):
    with open(CACHE_FILE, "w", encoding="utf-8") as f:
        f.write(text)
    print(f"\nLyrics cached in {CACHE_FILE}\n")

# ---------------------------------------------------------
# Load lyrics from cache
# ---------------------------------------------------------
def load_cache():
    global all_lyrics
    if os.path.exists(CACHE_FILE):
        print(f"\nLoading cached lyrics from {CACHE_FILE}...")
        with open(CACHE_FILE, "r", encoding="utf-8") as f:
            all_lyrics = f.read()
        return True
    return False

# ---------------------------------------------------------
# Fetch Ethel Cain lyrics
# ---------------------------------------------------------
def fetch_lyrics(force=False):
    global all_lyrics, words, markov

    # Try cache first unless forced
    if not force and load_cache():
        print("Cache loaded successfully.")
    else:
        print("\nFetching Ethel Cain songs from Genius...")
        artist = genius.search_artist("Ethel Cain", max_songs=50)

        all_lyrics = ""
        for song in artist.songs:
            if song.lyrics:
                print(f"Collected: {song.title}")
                all_lyrics += song.lyrics + "\n"

        save_cache(all_lyrics)

    # Clean lyrics → word list
    clean = re.sub(r"[^a-zA-Z\s]", "", all_lyrics).lower()
    words = clean.split()

    print(f"\nTotal words collected: {len(words)}")

    # Build Markov chain
    markov = {}
    for i in range(len(words) - 1):
        word = words[i]
        next_word = words[i + 1]
        if word not in markov:
            markov[word] = []
        markov[word].append(next_word)

    print("Markov chain built successfully.\n")

# ---------------------------------------------------------
# Generate a sentence using only her words
# ---------------------------------------------------------
def generate_sentence(length=12):
    if not words:
        return "Error: No lyrics loaded yet. Choose option 1 first."

    word = random.choice(words)
    sentence = [word]

    for _ in range(length - 1):
        if word in markov:
            word = random.choice(markov[word])
        else:
            word = random.choice(words)
        sentence.append(word)

    return " ".join(sentence)

# ---------------------------------------------------------
# CLI Menu
# ---------------------------------------------------------
def menu():
    while True:
        print("\n==============================")
        print("  Ethel Cain Sentence Maker")
        print("==============================")
        print("1. Load lyrics (cached or fetch)")
        print("2. Force refetch lyrics")
        print("3. Generate a sentence")
        print("4. Generate multiple sentences")
        print("5. Exit")
        print("==============================")

        choice = input("Choose an option: ").strip()

        if choice == "1":
            fetch_lyrics(force=False)

        elif choice == "2":
            fetch_lyrics(force=True)

        elif choice == "3":
            length = input("Sentence length (default 15): ").strip()
            length = int(length) if length.isdigit() else 15
            print("\nGenerated sentence:")
            print(generate_sentence(length))

        elif choice == "4":
            count = input("How many sentences? ").strip()
            count = int(count) if count.isdigit() else 5
            length = input("Sentence length (default 15): ").strip()
            length = int(length) if length.isdigit() else 15

            print("\nGenerated sentences:\n")
            for _ in range(count):
                print("-", generate_sentence(length))

        elif choice == "5":
            print("Goodbye.")
            sys.exit(0)

        else:
            print("Invalid choice. Try again.")

# ---------------------------------------------------------
# Run menu
# ---------------------------------------------------------
if __name__ == "__main__":
    menu()

Second generator here which generates sentences taken directly from lyrics, while the above generator takes individual words to generate sentences

import lyricsgenius
import re
import random

# ---------------------------------------------------------
# Insert your Genius API token directly here
# ---------------------------------------------------------
GENIUS_ACCESS_TOKEN = "GENIUS_TOKEN_HERE"

# ---------------------------------------------------------
# Initialize Genius API client
# ---------------------------------------------------------
genius = lyricsgenius.Genius(
    GENIUS_ACCESS_TOKEN,
    remove_section_headers=True,
    skip_non_songs=True,
    excluded_terms=["(Remix)", "(Live)"],
    timeout=15
)

# Global storage
all_lyrics = ""
sentences = []

# ---------------------------------------------------------
# Fetch lyrics
# ---------------------------------------------------------
def fetch_lyrics():
    global all_lyrics, sentences

    print("\nFetching Ethel Cain songs from Genius...")
    artist = genius.search_artist("Ethel Cain", max_songs=50)

    all_lyrics = ""
    for song in artist.songs:
        if song.lyrics:
            print(f"Collected: {song.title}")
            all_lyrics += song.lyrics + "\n"

    sentences = re.split(r'[.!?]\s+', all_lyrics)
    sentences = [s.strip() for s in sentences if len(s.strip()) > 0]

    print(f"\nTotal sentences extracted: {len(sentences)}")


# ---------------------------------------------------------
# Generate random sentences
# ---------------------------------------------------------
def generate_random_sentences():
    if not sentences:
        print("\nNo lyrics loaded yet. Choose option 1 first.")
        return

    try:
        n = int(input("How many random sentences? "))
    except ValueError:
        print("Invalid number.")
        return

    print("\nRandom sentences:")
    for _ in range(n):
        print("-", random.choice(sentences))


# ---------------------------------------------------------
# CLI Menu
# ---------------------------------------------------------
def menu():
    while True:
        print("\n=== Ethel Cain Sentence Generator ===")
        print("1. Fetch lyrics")
        print("2. Generate random sentences")
        print("3. Show number of extracted sentences")
        print("4. Exit")

        choice = input("Select an option: ")

        if choice == "1":
            fetch_lyrics()
        elif choice == "2":
            generate_random_sentences()
        elif choice == "3":
            print(f"\nSentences available: {len(sentences)}")
        elif choice == "4":
            print("Goodbye.")
            break
        else:
            print("Invalid option.")


# ---------------------------------------------------------
# Start CLI
# ---------------------------------------------------------
if __name__ == "__main__":
    menu()