Compare commits

..

No commits in common. '971c1777fd7576c2297b2a5d52e0d8559de8aaa5' and 'f45fe56f94998d6f07670d541f44f1cf88953acc' have entirely different histories.

@ -1,46 +1,2 @@
# letters # letters
A Python app that displays random historic love letters from authentic sources, downloaded from [Project Gutenberg](https://www.gutenberg.org/).
## Quick Start
```bash
python3 love_letters.py # Show a random love letter
python3 love_letters.py -n 3 # Show 3 random love letters
python3 love_letters.py --list # List available collections
python3 love_letters.py --source keats_brawne # Filter by source
```
The `letters/` directory ships with 1,300+ pre-parsed letters — no download needed.
## Refreshing / Adding Sources
To re-download all letter collections from Project Gutenberg:
```bash
python3 download_letters.py # Download (skips existing)
python3 download_letters.py --force # Re-download everything
python3 download_letters.py --list # List available sources
```
## Sources
| Collection | Author → Recipient | Period | Letters |
|---|---|---|---|
| Henry VIII to Anne Boleyn | Henry VIII → Anne Boleyn | c. 15271528 | 18 |
| Mary Wollstonecraft to Gilbert Imlay | Mary Wollstonecraft → Gilbert Imlay | 17931795 | 75 |
| Letters of Abelard and Heloise | Abelard & Heloise | 12th century | 6 |
| Napoleon's Letters to Josephine | Napoleon Bonaparte → Josephine | 17961812 | 395 |
| Letters of John Keats to Fanny Brawne | John Keats → Fanny Brawne | 18191820 | 39 |
| Robert Browning & Elizabeth Barrett Barrett, Vol. 1 | Browning ↔ Barrett | 18451846 | 281 |
| Robert Browning & Elizabeth Barrett Barrett, Vol. 2 | Browning ↔ Barrett | 18451846 | 292 |
| Robert Burns to Clarinda | Robert Burns → Agnes McLehose | 17871794 | 60 |
| Dorothy Osborne to Sir William Temple | Dorothy Osborne → William Temple | 16521654 | 51 |
| Beethoven's Letters (love letters selected) | Ludwig van Beethoven | 17901826 | 30 |
| Mozart's Letters (love letters selected) | Wolfgang Amadeus Mozart | 17691791 | 60 |
All texts are sourced from [Project Gutenberg](https://www.gutenberg.org/) and are in the public domain.
## Requirements
Python 3.10+ (no external dependencies).

@ -1,549 +0,0 @@
#!/usr/bin/env python3
"""
Download and parse love letters from Project Gutenberg.
This script fetches letter collections from Gutenberg, extracts individual
letters, and saves them as JSON files in the letters/ directory. Run this
once (or with --force to re-download) to populate the data that
love_letters.py reads.
Usage:
python3 download_letters.py # download all sources
python3 download_letters.py --force # re-download everything
python3 download_letters.py --list # show available sources
"""
import json
import os
import re
import sys
import urllib.request
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
LETTERS_DIR = os.path.join(SCRIPT_DIR, "letters")
SOURCES = [
{
"id": "henry_viii",
"title": "The Love Letters of Henry VIII to Anne Boleyn",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"year": "c. 15271528",
"url": "https://www.gutenberg.org/cache/epub/32155/pg32155.txt",
"gutenberg_id": 32155,
},
{
"id": "wollstonecraft",
"title": "The Love Letters of Mary Wollstonecraft to Gilbert Imlay",
"author": "Mary Wollstonecraft",
"recipient": "Gilbert Imlay",
"year": "17931795",
"url": "https://www.gutenberg.org/cache/epub/34413/pg34413.txt",
"gutenberg_id": 34413,
},
{
"id": "abelard_heloise",
"title": "Letters of Abelard and Heloise",
"author": "Abelard & Heloise",
"recipient": "each other",
"year": "12th century",
"url": "https://www.gutenberg.org/cache/epub/35977/pg35977.txt",
"gutenberg_id": 35977,
},
{
"id": "napoleon",
"title": "Napoleon's Letters to Josephine",
"author": "Napoleon Bonaparte",
"recipient": "Josephine",
"year": "17961812",
"url": "https://www.gutenberg.org/cache/epub/37499/pg37499.txt",
"gutenberg_id": 37499,
},
{
"id": "keats_brawne",
"title": "Letters of John Keats to Fanny Brawne",
"author": "John Keats",
"recipient": "Fanny Brawne",
"year": "18191820",
"url": "https://www.gutenberg.org/cache/epub/60433/pg60433.txt",
"gutenberg_id": 60433,
},
{
"id": "browning",
"title": "The Letters of Robert Browning and Elizabeth Barrett Barrett, Vol. 1",
"author": "Robert Browning & Elizabeth Barrett Browning",
"recipient": "each other",
"year": "18451846",
"url": "https://www.gutenberg.org/cache/epub/16182/pg16182.txt",
"gutenberg_id": 16182,
},
{
"id": "browning_vol2",
"title": "The Letters of Robert Browning and Elizabeth Barrett Barrett, Vol. 2",
"author": "Robert Browning & Elizabeth Barrett Browning",
"recipient": "each other",
"year": "18451846",
"url": "https://www.gutenberg.org/cache/epub/73891/pg73891.txt",
"gutenberg_id": 73891,
},
{
"id": "burns_clarinda",
"title": "Letters of Robert Burns to Clarinda",
"author": "Robert Burns",
"recipient": "Clarinda (Agnes McLehose)",
"year": "17871794",
"url": "https://www.gutenberg.org/cache/epub/9863/pg9863.txt",
"gutenberg_id": 9863,
},
{
"id": "dorothy_osborne",
"title": "The Love Letters of Dorothy Osborne to Sir William Temple",
"author": "Dorothy Osborne",
"recipient": "Sir William Temple",
"year": "16521654",
"url": "https://www.gutenberg.org/cache/epub/12544/pg12544.txt",
"gutenberg_id": 12544,
},
{
"id": "beethoven",
"title": "Beethoven's Letters 1790-1826, Volume 1",
"author": "Ludwig van Beethoven",
"recipient": "various (love letters selected)",
"year": "17901826",
"url": "https://www.gutenberg.org/cache/epub/13065/pg13065.txt",
"gutenberg_id": 13065,
},
{
"id": "mozart",
"title": "The Letters of Wolfgang Amadeus Mozart, Volume 1",
"author": "Wolfgang Amadeus Mozart",
"recipient": "various (love letters selected)",
"year": "17691791",
"url": "https://www.gutenberg.org/cache/epub/5307/pg5307.txt",
"gutenberg_id": 5307,
},
]
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def download_text(url: str) -> str:
"""Download a plain-text file from Project Gutenberg."""
req = urllib.request.Request(url, headers={"User-Agent": "LoveLettersApp/1.0"})
with urllib.request.urlopen(req, timeout=30) as resp:
return resp.read().decode("utf-8", errors="replace")
def strip_gutenberg(text: str) -> str:
"""Remove Project Gutenberg header and footer boilerplate."""
for marker in [
"*** START OF THE PROJECT GUTENBERG EBOOK",
"*** START OF THIS PROJECT GUTENBERG EBOOK",
"***START OF THE PROJECT GUTENBERG EBOOK",
]:
idx = text.find(marker)
if idx != -1:
nl = text.find("\n", idx)
text = text[nl + 1:] if nl != -1 else text[idx + len(marker):]
break
for marker in [
"*** END OF THE PROJECT GUTENBERG EBOOK",
"*** END OF THIS PROJECT GUTENBERG EBOOK",
"***END OF THE PROJECT GUTENBERG EBOOK",
"End of the Project Gutenberg EBook",
"End of Project Gutenberg",
]:
idx = text.find(marker)
if idx != -1:
text = text[:idx]
break
return text.strip()
def normalize(text: str) -> str:
"""Normalize line endings."""
return text.replace("\r\n", "\n")
# ---------------------------------------------------------------------------
# Per-source extractors
# ---------------------------------------------------------------------------
def extract_henry_viii(text: str) -> list[dict]:
text = normalize(strip_gutenberg(text))
parts = re.split(
r"\n{2,}(?=Letter\s+(?:First|Second|Third|Fourth|Fifth|Sixth|Seventh|"
r"Eighth|Ninth|Tenth|Eleventh|Twelfth|Thirteenth|Fourteenth|"
r"Fifteenth|Sixteenth|Seventeenth|Eighteenth)\b)",
text,
)
letters = []
for part in parts:
part = part.strip()
m = re.match(r"(Letter\s+\w+)(?:\s+.*?)?\n", part, re.IGNORECASE)
if not m or len(part) < 80:
continue
heading = m.group(1)
body = part[m.end():].strip()
for tag in ["\nNotes\n", "\nNOTES\n"]:
idx = body.find(tag)
if idx != -1:
body = body[:idx].strip()
author, recipient = "Henry VIII", "Anne Boleyn"
if "Boleyn to" in part[:200]:
author, recipient = "Anne Boleyn", "Cardinal Wolsey"
if len(body) > 50:
letters.append({
"heading": heading, "body": body,
"author": author, "recipient": recipient,
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528",
})
return letters
def extract_wollstonecraft(text: str) -> list[dict]:
text = normalize(strip_gutenberg(text))
parts = re.split(r"\n{2,}(?=LETTER\s+[IVXLC0-9]+\.?\s*\n)", text, flags=re.IGNORECASE)
letters = []
for part in parts:
part = part.strip()
m = re.match(r"(LETTER\s+[IVXLC0-9]+\.?)\s*\n", part, re.IGNORECASE)
if not m or len(part) < 80:
continue
body = part[m.end():].strip()
if len(body) > 50:
letters.append({
"heading": m.group(1), "body": body,
"author": "Mary Wollstonecraft", "recipient": "Gilbert Imlay",
"source": "The Love Letters of Mary Wollstonecraft to Gilbert Imlay",
"period": "17931795",
})
return letters
def extract_abelard_heloise(text: str) -> list[dict]:
text = normalize(strip_gutenberg(text))
parts = re.split(r"\n{2,}(?=LETTER\s+[IVXLC0-9]+[.:]?\s*\n)", text, flags=re.IGNORECASE)
letters = []
for part in parts:
part = part.strip()
m = re.match(r"(LETTER\s+[IVXLC0-9]+[.:]?)\s*\n", part, re.IGNORECASE)
if not m or len(part) < 120:
continue
body = part[m.end():].strip()
author, recipient = "Abelard & Heloise", "each other"
lower = body[:300].lower()
if "heloise to abelard" in lower:
author, recipient = "Heloise", "Abelard"
elif "abelard to heloise" in lower:
author, recipient = "Abelard", "Heloise"
if len(body) > 50:
letters.append({
"heading": m.group(1), "body": body,
"author": author, "recipient": recipient,
"source": "Letters of Abelard and Heloise",
"period": "12th century",
})
return letters
def extract_napoleon(text: str) -> list[dict]:
text = normalize(strip_gutenberg(text))
parts = re.split(r"\n{2,}(?=No\.\s*\d+\.\s*\n)", text)
letters = []
for part in parts:
part = part.strip()
m = re.match(r"(No\.\s*\d+\.)\s*\n", part)
if not m or len(part) < 100:
continue
body = part[m.end():].strip()
if len(body) > 80:
letters.append({
"heading": m.group(1), "body": body,
"author": "Napoleon Bonaparte", "recipient": "Josephine",
"source": "Napoleon's Letters to Josephine, 17961812",
"period": "17961812",
})
return letters
def extract_keats_brawne(text: str) -> list[dict]:
text = normalize(strip_gutenberg(text))
parts = re.split(r"\n{2,}(?=[IVXLC]+\.\s*\n)", text)
letters = []
for part in parts:
part = part.strip()
m = re.match(r"([IVXLC]+)\.\s*\n", part)
if not m or len(part) < 100:
continue
body = part[m.end():].strip()
if len(body) > 50:
letters.append({
"heading": f"Letter {m.group(1)}", "body": body,
"author": "John Keats", "recipient": "Fanny Brawne",
"source": "Letters of John Keats to Fanny Brawne",
"period": "18191820",
})
return letters
def _extract_browning(text: str, vol_label: str) -> list[dict]:
"""Extract letters from a Browning correspondence volume."""
text = normalize(strip_gutenberg(text))
# Split on _R.B. to E.B.B._ or _E.B.B. to R.B._
parts = re.split(r"\n{2,}(?=_(?:R\.B\. to E\.B\.B\.|E\.B\.B\. to R\.B\.)_)", text)
letters = []
for part in parts:
part = part.strip()
m = re.match(r"_(R\.B\. to E\.B\.B\.|E\.B\.B\. to R\.B\.)_\s*\n", part)
if not m or len(part) < 100:
continue
direction = m.group(1)
body = part[m.end():].strip()
if "R.B. to E.B.B." in direction:
author = "Robert Browning"
recipient = "Elizabeth Barrett Browning"
else:
author = "Elizabeth Barrett Browning"
recipient = "Robert Browning"
if len(body) > 50:
letters.append({
"heading": direction, "body": body,
"author": author, "recipient": recipient,
"source": f"The Letters of Robert Browning and Elizabeth Barrett Barrett, {vol_label}",
"period": "18451846",
})
return letters
def extract_browning(text: str) -> list[dict]:
return _extract_browning(text, "Vol. 1")
def extract_browning_vol2(text: str) -> list[dict]:
return _extract_browning(text, "Vol. 2")
def extract_burns_clarinda(text: str) -> list[dict]:
text = normalize(strip_gutenberg(text))
# Find the "LETTERS TO CLARINDA" section
start_idx = text.find("LETTERS TO CLARINDA")
if start_idx == -1:
return []
section = text[start_idx:]
# End at next major section (all caps heading after blank lines)
end_match = re.search(r"\n{3,}[A-Z][A-Z ]{10,}\n", section[100:])
if end_match:
section = section[:100 + end_match.start()]
parts = re.split(r"\n{2,}(?=[IVXLC]+\.\s*\n)", section)
letters = []
for part in parts:
part = part.strip()
m = re.match(r"([IVXLC]+)\.\s*\n", part)
if not m or len(part) < 80:
continue
body = part[m.end():].strip()
if len(body) > 50:
letters.append({
"heading": f"Letter {m.group(1)}", "body": body,
"author": "Robert Burns", "recipient": "Clarinda (Agnes McLehose)",
"source": "Letters of Robert Burns to Clarinda",
"period": "17871794",
})
return letters
def extract_dorothy_osborne(text: str) -> list[dict]:
text = normalize(strip_gutenberg(text))
# Letters start with "SIR,--" after editorial commentary
# Split by looking backwards from each "SIR,--" to find the date/heading
letters = []
# Find all "SIR,--" occurrences
sir_positions = [m.start() for m in re.finditer(r"^SIR,--", text, re.MULTILINE)]
for i, pos in enumerate(sir_positions):
# Look for a date line just before the salutation
preceding = text[max(0, pos - 200):pos]
date_match = re.search(r"\n\n_([^_]+)_\.?\s*\n\s*$", preceding)
heading = date_match.group(1).strip() if date_match else ""
# Letter body extends to the next editorial section or next SIR
if i + 1 < len(sir_positions):
end = sir_positions[i + 1]
# Try to find where editorial notes begin (usually with _Letter)
editorial = re.search(r"\n_Letter\s+[IVXLC]+\._", text[pos:end])
if editorial:
end = pos + editorial.start()
else:
end = len(text)
body = text[pos:end].strip()
# Trim trailing editorial notes (paragraphs starting with special patterns)
body = re.split(r"\n\n(?=_[A-Z])", body)[0].strip()
if len(body) > 80:
letters.append({
"heading": heading if heading else f"Letter {i + 1}",
"body": body,
"author": "Dorothy Osborne", "recipient": "Sir William Temple",
"source": "The Love Letters of Dorothy Osborne to Sir William Temple",
"period": "16521654",
})
return letters
def extract_beethoven(text: str) -> list[dict]:
text = normalize(strip_gutenberg(text))
# Letters are headed "N.\n\nTO ..." where N is a number
parts = re.split(r"\n{2,}(?=\d+\.\s*\n\s*\nTO\s)", text)
letters = []
# Keywords to identify love/romantic letters
love_keywords = [
"immortal beloved", "my angel", "my love", "beloved",
"my heart", "kiss", "embrace", "love you",
"sweetheart", "giulietta", "guicciardi",
"josephine brunsvik", "bettina", "brentano",
"amalie sebald", "my all", "my second self",
"ardently", "passionately", "tenderly yours",
]
for part in parts:
part = part.strip()
m = re.match(r"(\d+)\.\s*\n\s*\n(TO\s+.+?)(?:\n|$)", part)
if not m or len(part) < 100:
continue
num = m.group(1)
to_line = m.group(2).strip()
body = part[m.end():].strip()
full_text = (to_line + " " + body).lower()
# Only include letters with romantic content
if any(kw in full_text for kw in love_keywords):
letters.append({
"heading": f"No. {num}{to_line}",
"body": body,
"author": "Ludwig van Beethoven",
"recipient": to_line.replace("TO ", "").strip("."),
"source": "Beethoven's Letters 17901826",
"period": "17901826",
})
return letters
def extract_mozart(text: str) -> list[dict]:
text = normalize(strip_gutenberg(text))
# Mozart's letters use numbered sections
parts = re.split(r"\n{2,}(?=\d+\.\s*\n)", text)
letters = []
love_keywords = [
"my love", "kiss", "beloved", "my heart",
"my dear wife", "constanze", "my darling",
"embrace you", "tender", "passionately",
"aloysia", "my dearest wife",
]
for part in parts:
part = part.strip()
m = re.match(r"(\d+)\.\s*\n", part)
if not m or len(part) < 100:
continue
num = m.group(1)
body = part[m.end():].strip()
# Extract TO line if present
to_match = re.match(r"(TO\s+.+?)(?:\n|$)", body)
to_line = to_match.group(1).strip() if to_match else ""
full_text = body.lower()
if any(kw in full_text for kw in love_keywords):
recipient = to_line.replace("TO ", "").strip(".") if to_line else "Constanze Mozart"
letters.append({
"heading": f"No. {num}" + (f"{to_line}" if to_line else ""),
"body": body,
"author": "Wolfgang Amadeus Mozart",
"recipient": recipient,
"source": "The Letters of Wolfgang Amadeus Mozart",
"period": "17691791",
})
return letters
EXTRACTORS = {
"henry_viii": extract_henry_viii,
"wollstonecraft": extract_wollstonecraft,
"abelard_heloise": extract_abelard_heloise,
"napoleon": extract_napoleon,
"keats_brawne": extract_keats_brawne,
"browning": extract_browning,
"browning_vol2": extract_browning_vol2,
"burns_clarinda": extract_burns_clarinda,
"dorothy_osborne": extract_dorothy_osborne,
"beethoven": extract_beethoven,
"mozart": extract_mozart,
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def download_source(source: dict, force: bool = False) -> int:
"""Download, parse, and save letters for one source. Returns letter count."""
out_path = os.path.join(LETTERS_DIR, f"{source['id']}.json")
if os.path.exists(out_path) and not force:
existing = json.load(open(out_path, "r", encoding="utf-8"))
return len(existing)
print(f" ⬇ Downloading: {source['title']}", flush=True)
try:
raw = download_text(source["url"])
except Exception as e:
print(f" ⚠ Failed: {e}")
return 0
extractor = EXTRACTORS.get(source["id"])
if extractor is None:
print(f" ⚠ No extractor for {source['id']}")
return 0
letters = extractor(raw)
if not letters:
print(f" ⚠ No letters extracted from {source['title']}")
return 0
os.makedirs(LETTERS_DIR, exist_ok=True)
with open(out_path, "w", encoding="utf-8") as f:
json.dump(letters, f, ensure_ascii=False, indent=2)
print(f"{len(letters)} letters saved → letters/{source['id']}.json")
return len(letters)
def main() -> None:
import argparse
parser = argparse.ArgumentParser(description="Download love letters from Project Gutenberg.")
parser.add_argument("--force", action="store_true", help="re-download all sources")
parser.add_argument("--list", action="store_true", help="list available sources")
args = parser.parse_args()
if args.list:
print("\n Available sources:\n")
for i, src in enumerate(SOURCES, 1):
print(f" {i:2}. [{src['id']}] {src['title']}")
print(f" {src['author']}{src['recipient']} ({src['year']})")
print(f" gutenberg.org/ebooks/{src['gutenberg_id']}")
print()
return
print("\n 📥 Downloading love letters from Project Gutenberg…\n")
total = 0
for source in SOURCES:
count = download_source(source, force=args.force)
total += count
print(f"\n 📬 Total: {total} letters in {LETTERS_DIR}/\n")
if __name__ == "__main__":
main()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,146 +0,0 @@
[
{
"heading": "Letter First",
"body": "On turning over in my mind the contents of your last letters, I have put\nmyself into great agony, not knowing how to interpret them, whether to my\ndisadvantage, as you show in some places, or to my advantage, as I\nunderstand them in some others, beseeching you earnestly to let me know\nexpressly your whole mind as to the love between us two. It is absolutely\nnecessary for me to obtain this answer, having been for above a whole year\nstricken with the dart of love, and not yet sure whether I shall fail of\nfinding a place in your heart and affection, which last point has\nprevented me for some time past from calling you my mistress; because, if\nyou only love me with an ordinary love, that name is not suitable for you,\nbecause it denotes a singular love, which is far from common. But if you\nplease to do the office of a true loyal mistress and friend, and to give\nup yourself body and heart to me, who will be, and have been, your most\nloyal servant, (if your rigour does not forbid me) I promise you that not\nonly the name shall be given you, but also that I will take you for my\nonly mistress, casting off all others besides you out of my thoughts and\naffections, and serve you only. I beseech you to give an entire answer to\nthis my rude letter, that I may know on what and how far I may depend. And\nif it does not please you to answer me in writing, appoint some place\nwhere I may have it by word of mouth, and I will go thither with all my\nheart. No more, for fear of tiring you. Written by the hand of him who\nwould willingly remain yours,\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Second",
"body": "Though it is not fitting for a gentleman to take his lady in the place of\na servant, yet, complying with your desire, I willingly grant it you, if\nthereby you can find yourself less uncomfortable in the place chosen by\nyourself, than you have been in that which I gave you, thanking you\ncordially that you are pleased still to have some remembrance of me. 6. n.\nA. 1 de A. o. na. v. e. z.\n\nHENRY R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Third",
"body": "Although, my Mistress, it has not pleased you to remember the promise you\nmade me when I was last with you--that is, to hear good news from you, and\nto have an answer to my last letter; yet it seems to me that it belongs to\na true servant (seeing that otherwise he can know nothing) to inquire the\nhealth of his mistress, and to acquit myself of the duty of a true\nservant, I send you this letter, beseeching you to apprise me of your\nwelfare, which I pray to God may continue as long as I desire mine own.\nAnd to cause you yet oftener to remember me, I send you, by the bearer of\nthis, a buck killed late last night by my own hand, hoping that when you\neat of it you may think of the hunter; and thus, for want of room, I must\nend my letter, written by the hand of your servant, who very often wishes\nfor you instead of your brother.\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Fourth",
"body": "_MY MISTRESS & FRIEND_, my heart and I surrender ourselves into your\nhands, beseeching you to hold us commended to your favour, and that by\nabsence your affection to us may not be lessened: for it were a great pity\nto increase our pain, of which absence produces enough and more than I\ncould ever have thought could be felt, reminding us of a point in\nastronomy which is this: the longer the days are, the more distant is the\nsun, and nevertheless the hotter; so is it with our love, for by absence\nwe are kept a distance from one another, and yet it retains its fervour,\nat least on my side; I hope the like on yours, assuring you that on my\npart the pain of absence is already too great for me; and when I think of\nthe increase of that which I am forced to suffer, it would be almost\nintolerable, but for the firm hope I have of your unchangeable affection\nfor me: and to remind you of this sometimes, and seeing that I cannot be\npersonally present with you, I now send you the nearest thing I can to\nthat, namely, my picture set in a bracelet, with the whole of the device,\nwhich you already know, wishing myself in their place, if it should please\nyou. This is from the hand of your loyal servant and friend,\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Fifth",
"body": "For a present so beautiful that nothing could be more so (considering the\nwhole of it), I thank you most cordially, not only on account of the fine\ndiamond and the ship in which the solitary damsel is tossed about, but\nchiefly for the fine interpretation and the too humble submission which\nyour goodness hath used towards me in this case; for I think it would be\nvery difficult for me to find an occasion to deserve it, if I were not\nassisted by your great humanity and favour, which I have always sought to\nseek, and will seek to preserve by all the kindness in my power, in which\nmy hope has placed its unchangeable intention, which says, _Aut illic, aut\nnullibi_.\n\nThe demonstrations of your affection are such, the beautiful mottoes of\nthe letter so cordially expressed, that they oblige me for ever to honour,\nlove, and serve you sincerely, beseeching you to continue in the same firm\nand constant purpose, assuring you that, on my part, I will surpass it\nrather than make it reciprocal, if loyalty of heart and a desire to please\nyou can accomplish this.\n\nI beg, also, if at any time before this I have in any way offended you,\nthat you would give me the same absolution that you ask, assuring you,\nthat henceforward my heart shall be dedicated to you alone. I wish my\nperson was so too. God can do it, if He pleases, to whom I pray every day\nfor that end, hoping that at length my prayers will be heard. I wish the\ntime may be short, but I shall think it long till we see one another.\n\nWritten by the hand of that secretary, who in heart, body, and will, is,\n\nYour loyal and most assured Servant,\n\nH. sultre A.B. ne cherse R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Sixth",
"body": "_TO MY MISTRESS._ Because the time seems very long since I heard\nconcerning your health and you, the great affection I have for you has\ninduced me to send you this bearer, to be better informed of your health\nand pleasure, and because, since my parting from you, I have been told\nthat the opinion in which I left you is totally changed, and that you\nwould not come to court either with your mother, if you could, or in any\nother manner; which report, if true, I cannot sufficiently marvel at,\nbecause I am sure that I have since never done any thing to offend you,\nand it seems a very poor return for the great love which I bear you to\nkeep me at a distance both from the speech and the person of the woman\nthat I esteem most in the world: and if you love me with as much affection\nas I hope you do, I am sure that the distance of our two persons would be\na little irksome to you, though this does not belong so much to the\nmistress as to the servant.\n\nConsider well, my mistress, that absence from you grieves me sorely,\nhoping that it is not your will that it should be so; but if I knew for\ncertain that you voluntarily desired it, I could do no other than mourn\nmy ill-fortune, and by degrees abate my great folly. And so, for lack of\ntime, I make an end of this rude letter, beseeching you to give credence\nto this bearer in all that he will tell you from me.\n\nWritten by the hand of your entire Servant,\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Seventh",
"body": "_DARLING_, these shall be only to advertise you that this bearer and his\nfellow be despatched with as many things to compass our matter, and to\nbring it to pass as our wits could imagine or devise; which brought to\npass, as I trust, by their diligence, it shall be shortly, you and I shall\nhave our desired end, which should be more to my heart's ease, and more\nquietness to my mind, than any other thing in the world; as, with God's\ngrace, shortly I trust shall be proved, but not so soon as I would it\nwere; yet I will ensure you that there shall be no time lost that may be\nwon, and further can not be done; for _ultra posse non est esse_. Keep him\nnot too long with you, but desire him, for your sake, to make the more\nspeed; for the sooner we shall have word from him, the sooner shall our\nmatter come to pass. And thus upon trust of your short repair to London, I\nmake an end of my letter, my own sweet heart.\n\nWritten with the hand of him which desireth as much to be yours as you do\nto have him.\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Eighth",
"body": "_MY LORD_, in my most humblest wise that my heart can think, I desire you\nto pardon me that I am so bold to trouble you with my simple and rude\nwriting, esteeming it to proceed from her that is much desirous to know\nthat your grace does well, as I perceive by this bearer that you do, the\nwhich I pray God long to continue, as I am most bound to pray; for I do\nknow the great pains and troubles that you have taken for me both day and\nnight is never likely to be recompensed on my part, but alonely in loving\nyou, next unto the king's grace, above all creatures living. And I do not\ndoubt but the daily proofs of my deeds shall manifestly declare and affirm\nmy writing to be true, and I do trust you do think the same.\n\nMy lord, I do assure you, I do long to hear from you news of the legate;\nfor I do hope, as they come from you, they shall be very good; and I am\nsure you desire it as much as I, and more, an it were possible; as I know\nit is not: and thus remaining in a steadfast hope, I make an end of my\nletter.\n\nWritten with the hand of her that is most bound to be\n\nYour humble Servant,\n\nANNE BOLEYN.\n\n\n\n\nPostscript by Henry viii\n\n\nThe writer of this letter would not cease, till she had caused me likewise\nto set my hand, desiring you, though it be short, to take it in good part.\nI ensure you that there is neither of us but greatly desireth to see you,\nand are joyous to hear that you have escaped this plague so well, trusting\nthe fury thereof to be passed, especially with them that keepeth good\ndiet, as I trust you do. The not hearing of the legate's arrival in France\ncauseth us somewhat to muse; notwithstanding, we trust, by your diligence\nand vigilancy (with the assistance of Almighty God), shortly to be eased\nout of that trouble. No more to you at this time, but that I pray God send\nyou as good health and prosperity as the writer would.\n\nBy your loving Sovereign and Friend,\n\nH. R.",
"author": "Anne Boleyn",
"recipient": "Cardinal Wolsey",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Ninth",
"body": "There came to me suddenly in the night the most afflicting news that could\nhave arrived. The first, to hear of the sickness of my mistress, whom I\nesteem more than all the world, and whose health I desire as I do my own,\nso that I would gladly bear half your illness to make you well. The\nsecond, from the fear that I have of being still longer harassed by my\nenemy, Absence, much longer, who has hitherto given me all possible\nuneasiness, and as far as I can judge is determined to spite me more\nbecause I pray God to rid me of this troublesome tormentor. The third,\nbecause the physician in whom I have most confidence, is absent at the\nvery time when he might do me the greatest pleasure; for I should hope, by\nhim and his means, to obtain one of my chief joys on earth--that is the\ncare of my mistress--yet for want of him I send you my second, and hope\nthat he will soon make you well. I shall then love him more than ever. I\nbeseech you to be guided by his advice in your illness. In so doing I hope\nsoon to see you again, which will be to me a greater comfort than all the\nprecious jewels in the world.\n\nWritten by that secretary, who is, and for ever will be, your loyal and\nmost assured Servant,\n\nH. (A B) R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Tenth",
"body": "The uneasiness my doubts about your health gave me, disturbed and alarmed\nme exceedingly, and I should not have had any quiet without hearing\ncertain tidings. But now, since you have as yet felt nothing, I hope, and\nam assured that it will spare you, as I hope it is doing with us. For when\nwe were at Walton, two ushers, two valets de chambres and your brother,\nmaster-treasurer, fell ill, but are now quite well; and since we have\nreturned to our house at Hunsdon, we have been perfectly well, and have\nnot, at present, one sick person, God be praised; and I think, if you\nwould retire from Surrey, as we did, you would escape all danger. There is\nanother thing that may comfort you, which is, that, in truth in this\ndistemper few or no women have been taken ill, and what is more, no person\nof our court, and few elsewhere, have died of it. For which reason I beg\nyou, my entirely beloved, not to frighten yourself nor be too uneasy at\nour absence; for wherever I am, I am yours, and yet we must sometimes\nsubmit to our misfortunes, for whoever will struggle against fate is\ngenerally but so much the farther from gaining his end: wherefore comfort\nyourself, and take courage and avoid the pestilence as much as you can,\nfor I hope shortly to make you sing, _la renvoye_. No more at present,\nfrom lack of time, but that I wish you in my arms, that I might a little\ndispel your unreasonable thoughts.\n\nWritten by the hand of him who is and alway will be yours,\n\nIm- H. R. -mutable.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Eleventh",
"body": "The cause of my writing at this time, good sweetheart, is only to\nunderstand of your good health and prosperity; whereof to know I would be\nas glad as in manner mine own, praying God that (an it be His pleasure) to\nsend us shortly together, for I promise you I long for it. How be it, I\ntrust it shall not be long to; and seeing my darling is absent, I can do\nno less than to send her some flesh, representing my name, which is hart\nflesh for Henry, prognosticating that hereafter, God willing, you may\nenjoy some of mine, which He pleased, I would were now.\n\nAs touching your sister's matter, I have caused Walter Welze to write to\nmy lord my mind therein, whereby I trust that Eve shall not have power to\ndeceive Adam; for surely, whatsoever is said, it cannot so stand with his\nhonour but that he must needs take her, his natural daughter, now in her\nextreme necessity.\n\nNo more to you at this time, mine own darling, but that with a wish I\nwould we were together an evening.\n\nWith the hand of yours,\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Twelfth",
"body": "Since your last letters, mine own darling, Walter Welshe, Master Browne,\nThos. Care, Grion of Brearton, and John Coke, the apothecary, be fallen of\nthe sweat in this house, and, thanked be God, all well recovered, so that\nas yet the plague is not fully ceased here, but I trust shortly it shall.\nBy the mercy of God, the rest of us yet be well, and I trust shall pass\nit, either not to have it, or, at the least, as easily as the rest have\ndone.\n\nAs touching the matter of Wilton, my lord cardinal hath had the nuns\nbefore him, and examined them, Mr. Bell being present; which hath\ncertified me that, for a truth, she had confessed herself (which we would\nhave had abbess) to have had two children by two sundry priests; and,\nfurther, since hath been kept by a servant of the Lord Broke that was, and\nthat not long ago. Wherefore I would not, for all the gold in the world,\nclog your conscience nor mine to make her ruler of a house which is of so\nungodly demeanour; nor, I trust, you would not that neither for brother\nnor sister, I should so destain mine honour or conscience. And, as\ntouching the prioress, or Dame Eleanor's eldest sister, though there is\nnot any evident case proved against them, and that the prioress is so old\nthat for many years she could not be as she was named; yet\nnotwithstanding, to do you pleasure, I have done that neither of them\nshall have it, but that some other good and well-disposed woman shall have\nit, whereby the house shall be the better reformed (whereof I ensure you\nit had much need), and God much the better served.\n\nAs touching your abode at Hever, do therein as best shall like you, for\nyou best know what air doth best with you; but I would it were come\nthereto (if it pleased God), that neither of us need care for that, for I\nensure you I think it long. Suche is fallen sick of the sweat, and\ntherefore I send you this bearer, because I think you long to hear\ntidings from us, as we do likewise from you.\n\nWritten with the hand _de votre seul_,\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Thirteenth",
"body": "The approach of the time for which I have so long waited rejoices me so\nmuch, that it seems almost to have come already. However, the entire\naccomplishment cannot be till the two persons meet, which meeting is more\ndesired by me than anything in this world; for what joy can be greater\nupon earth than to have the company of her who is dearest to me, knowing\nlikewise that she does the same on her part, the thought of which gives me\nthe greatest pleasure.\n\nJudge what an effect the presence of that person must have on me, whose\nabsence has grieved my heart more than either words or writing can\nexpress, and which nothing can cure, but that begging you, my mistress, to\ntell your father from me, that I desire him to hasten the time appointed\nby two days, that he may be at court before the old term, or, at farthest,\non the day prefixed; for otherwise I shall think he will not do the\nlover's turn, as he said he would, nor answer my expectation.\n\nNo more at present for lack of time, hoping shortly that by word of mouth\nI shall tell you the rest of the sufferings endured by me from your\nabsence.\n\nWritten by the hand of the secretary, who wishes himself at this moment\nprivately with you, and who is, and always will be,\n\nYour loyal and most assured Servant,\n\nH. no other A B seek R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Fourteenth",
"body": "_DARLING_, I heartily recommend me to you, ascertaining you that I am not\na little perplexed with such things as your brother shall on my part\ndeclare unto you, to whom I pray you give full credence, for it were too\nlong to write. In my last letters I writ to you that I trusted shortly to\nsee you, which is better known at London than with any that is about me,\nwhereof I not a little marvel; but lack of discreet handling must needs be\nthe cause thereof. No more to you at this time, but that I trust shortly\nour meetings shall not depend upon other men's light handlings, but upon\nour own.\n\nWritten with the hand of him that longeth to be yours.\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Fifteenth",
"body": "_MINE own SWEETHEART_, this shall be to advertise you of the great\nelengeness that I find here since your departing; for, I ensure you\nmethinketh the time longer since your departing now last, than I was wont\nto do a whole fortnight. I think your kindness and my fervency of love\ncauseth it; for, otherwise, I would not have thought it possible that for\nso little a while it should have grieved me. But now that I am coming\ntowards you, methinketh my pains be half removed; and also I am right\nwell comforted in so much that my book maketh substantially for my\nmatter; in looking whereof I have spent above four hours this day, which\ncauseth me now to write the shorter letter to you at this time, because of\nsome pain in my head; wishing myself (especially an evening) in my\nsweetheart's arms, whose pretty dukkys I trust shortly to kiss.\n\nWritten by the hand of him that was, is, and shall be yours by his own\nwill,\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Sixteenth",
"body": "_DARLING_, Though I have scant leisure, yet, remembering my promise, I\nthought it convenient to certify you briefly in what case our affairs\nstand. As touching a lodging for you, we have got one by my lord\ncardinal's means, the like whereof could not have been found hereabouts\nfor all causes, as this bearer shall more show you. As touching our other\naffairs, I assure you there can be no more done, nor more diligence used,\nnor all manner of dangers better both foreseen and provided for, so that\nI trust it shall be hereafter to both our comforts, the specialities\nwhereof were both too long to be written, and hardly by messenger to be\ndeclared. Wherefore, till you repair hither, I keep something in store,\ntrusting it shall not be long to; for I have caused my lord, your father,\nto make his provisions with speed; and thus for lack of time, darling, I\nmake an end of my letter, written with the hand of him which I would were\nyours.\n\nH. R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Seventeenth",
"body": "The reasonable request of your last letter, with the pleasure also that I\ntake to know them true, causeth me to send you these news. The legate\nwhich we most desire arrived at Paris on Sunday or Monday last past, so\nthat I trust by the next Monday to hear of his arrival at Calais: and then\nI trust within a while after to enjoy that which I have so long longed\nfor, to God's pleasure and our both comforts.\n\nNo more to you at this present, mine own darling, for lack of time, but\nthat I would you were in mine arms, or I in yours, for I think it long\nsince I kissed you.\n\nWritten after the killing of a hart, at eleven of the clock, minding, with\nGod's grace, to-morrow, mightily timely, to kill another, by the hand\nwhich, I trust, shortly shall be yours.\n\nHENRY R.",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
},
{
"heading": "Letter Eighteenth",
"body": "To inform you what joy it is to me to understand of your conformableness\nwith reason, and of the suppressing of your inutile and vain thoughts with\nthe bridle of reason. I assure you all the good in this world could not\ncounterpoise for my satisfaction the knowledge and certainty thereof,\nwherefore, good sweetheart, continue the same, not only in this, but in\nall your doings hereafter; for thereby shall come, both to you and me, the\ngreatest quietness that may be in this world.\n\nThe cause why the bearer stays so long, is the business I have had to\ndress up gear for you; and which I trust, ere long to cause you occupy:\nthen I trust to occupy yours, which shall be recompense enough to me for\nall my pains and labour.\n\nThe unfeigned sickness of this well-willing legate doth somewhat retard\nhis access to your person; but I trust verily, when God shall send him\nhealth, he will with diligence recompense his demur. For I know well where\nhe hath said (touching the saying and bruit that he is thought imperial)\nthat it shall be well known in this matter that he is not imperial; and\nthus, for lack of time, sweetheart, farewell.\n\nWritten with the hand which fain would be yours, and so is the heart.\n\nR. H.\n\n\n\nFinis",
"author": "Henry VIII",
"recipient": "Anne Boleyn",
"source": "The Love Letters of Henry VIII to Anne Boleyn",
"period": "c. 15271528"
}
]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,153 +0,0 @@
#!/usr/bin/env python3
"""
Love Letters Display random historic love letters from Project Gutenberg.
Reads pre-downloaded letter collections from the letters/ directory.
Run download_letters.py first to populate the data.
"""
import json
import os
import random
import re
import sys
import textwrap
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
LETTERS_DIR = os.path.join(SCRIPT_DIR, "letters")
def load_all_letters(source_filter: str | None = None) -> list[dict]:
"""Load all letters from JSON files in the letters/ directory."""
if not os.path.isdir(LETTERS_DIR):
return []
all_letters: list[dict] = []
for filename in sorted(os.listdir(LETTERS_DIR)):
if not filename.endswith(".json"):
continue
source_id = filename[:-5]
if source_filter and source_id != source_filter:
continue
path = os.path.join(LETTERS_DIR, filename)
with open(path, "r", encoding="utf-8") as f:
all_letters.extend(json.load(f))
return all_letters
def available_sources() -> list[str]:
"""Return list of source IDs from the letters/ directory."""
if not os.path.isdir(LETTERS_DIR):
return []
return sorted(
f[:-5] for f in os.listdir(LETTERS_DIR) if f.endswith(".json")
)
def wrap_text(text: str, width: int = 78) -> str:
"""Word-wrap text while preserving paragraph breaks."""
paragraphs = re.split(r"\n\s*\n", text)
wrapped = []
for para in paragraphs:
para = " ".join(para.split())
wrapped.append(textwrap.fill(para, width=width))
return "\n\n".join(wrapped)
def display_letter(letter: dict) -> None:
"""Pretty-print a single love letter to the terminal."""
print()
print(SEPARATOR)
print(f"{letter['author']}{letter['recipient']}")
if letter.get("heading"):
print(f" {letter['heading']}")
print(f" ({letter['period']})")
print(SEPARATOR)
print()
print(wrap_text(letter["body"]))
print()
print(SEPARATOR)
print(f" Source: {letter['source']}")
print(f" Via Project Gutenberg • gutenberg.org")
print(SEPARATOR)
print()
SEPARATOR = "" * 60
def list_sources() -> None:
"""Print available letter collections with counts."""
sources = available_sources()
if not sources:
print("\n No letter collections found. Run download_letters.py first.\n")
return
print("\n Available collections:\n")
for i, source_id in enumerate(sources, 1):
path = os.path.join(LETTERS_DIR, f"{source_id}.json")
with open(path, "r", encoding="utf-8") as f:
letters = json.load(f)
if not letters:
continue
first = letters[0]
print(f" {i:2}. [{source_id}]")
print(f" {first['author']}{first['recipient']} ({first['period']})")
print(f" {first['source']} ({len(letters)} letters)")
print()
def main() -> None:
import argparse
sources = available_sources()
parser = argparse.ArgumentParser(
description="Display random historic love letters from Project Gutenberg.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=textwrap.dedent("""\
examples:
%(prog)s Show a random love letter
%(prog)s -n 3 Show 3 random love letters
%(prog)s --list List available collections
%(prog)s --source keats_brawne Show only Keats letters
"""),
)
parser.add_argument(
"-n", "--count", type=int, default=1, metavar="N",
help="number of letters to display (default: 1)",
)
parser.add_argument(
"--list", action="store_true",
help="list available letter collections",
)
parser.add_argument(
"--source", type=str, metavar="ID",
choices=sources if sources else None,
help="only show letters from a specific source",
)
args = parser.parse_args()
if args.list:
list_sources()
return
all_letters = load_all_letters(source_filter=args.source)
if not all_letters:
if not sources:
print(" No letter data found. Run download_letters.py first.")
else:
print(f" No letters found for source '{args.source}'.")
sys.exit(1)
count = min(args.count, len(all_letters))
chosen = random.sample(all_letters, count)
for letter in chosen:
display_letter(letter)
if __name__ == "__main__":
main()
Loading…
Cancel
Save