diff --git a/adhd/deploy.sh b/adhd/deploy.sh
new file mode 100755
index 0000000..caab93b
--- /dev/null
+++ b/adhd/deploy.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+# Build (nothing to build yet — site is static) + sync docroot in place.
+set -euo pipefail
+cd "$(dirname "$0")"
+BASE=/home/opc/zai-home-base
+
+mkdir -p "$BASE/sites/myadhd.dev"
+rsync -a --delete site/ "$BASE/sites/myadhd.dev/"
+
+echo "==> verifying through Traefik (origin)"
+sleep 1
+code=$(curl -sk -o /dev/null -w '%{http_code}' --resolve myadhd.dev:443:127.0.0.1 https://myadhd.dev/ --max-time 10)
+echo " https://myadhd.dev -> $code (origin)"
+for asset in /static/style.css /static/app.js /static/favicon.svg /robots.txt /sitemap.xml; do
+ out=$(curl -sk -o /dev/null -w '%{http_code} %{content_type}' --resolve myadhd.dev:443:127.0.0.1 "https://myadhd.dev$asset" --max-time 10)
+ echo " $asset -> $out"
+done
+echo "==> deploy complete"
diff --git a/bdj/build.py b/bdj/build.py
index d69ebac..ff5c365 100644
--- a/bdj/build.py
+++ b/bdj/build.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""Static site generator for bestdadjokes.lol. Python 3.6 stdlib only."""
-import json, os, shutil, html, datetime
+import json, os, re, shutil, html, datetime
ROOT = os.path.dirname(os.path.abspath(__file__))
CONTENT = os.path.join(ROOT, 'content', 'jokes.json')
@@ -20,6 +20,41 @@ CAT_BY_SLUG = {c['slug']: c for c in CATS}
for i, j in enumerate(JOKES):
j['id'] = i
+# Long-tail topic pages: jokes matched by keyword across all categories.
+TOPICS = [
+ {"slug": "coffee", "name": "Dad Jokes About Coffee", "emoji": "☕",
+ "intro": "Fuel for dads and their worst puns alike. These coffee dad jokes are best served strong, black, and with a healthy amount of eye-rolling.",
+ "keywords": r"\b(coffee|espresso|latte|caffeine|beans?|mugs?)\b"},
+ {"slug": "cats", "name": "Dad Jokes About Cats", "emoji": "🐱",
+ "intro": "For the cat dads out there. These cat dad jokes will make your feline walk out of the room in disgust — which, for a cat, is a standing ovation.",
+ "keywords": r"\b(cats?|kittens?|purr|feline|meow)\b"},
+ {"slug": "dogs", "name": "Dad Jokes About Dogs", "emoji": "🐕",
+ "intro": "A dog is the only audience that wags at dad jokes. Fetch! These dog dad jokes are good boys, every one of them.",
+ "keywords": r"\b(dogs?|puppy|puppies|pup|hound)\b"},
+ {"slug": "cows", "name": "Dad Jokes About Cows", "emoji": "🐄",
+ "intro": "Outstanding in their field and in our pun archive. These cow dad jokes are udderly relentless.",
+ "keywords": r"\b(cows?|cattle|moo|udder|beef|herd)\b"},
+ {"slug": "pizza", "name": "Dad Jokes About Pizza", "emoji": "🍕",
+ "intro": "A slice of comedy gold. These pizza dad jokes are cheesy by design — that's not a bug, it's the whole point.",
+ "keywords": r"\bpizzas?\b"},
+ {"slug": "money", "name": "Dad Jokes About Money", "emoji": "💰",
+ "intro": "Money can't buy happiness, but it can buy these money dad jokes, which are basically the same thing but cheaper.",
+ "keywords": r"\b(money|cash|coins?|dollars?|penn(y|ies)|bank|rich|spen[dt]|salary)\b"},
+ {"slug": "sleep", "name": "Dad Jokes About Sleep", "emoji": "😴",
+ "intro": "Read these sleep dad jokes in bed. Groaning yourself to sleep counts as a lullaby.",
+ "keywords": r"\b(sleep|nap|bed|pillow|blanket|tired|asleep|dream)\b"},
+ {"slug": "love", "name": "Dad Jokes About Love", "emoji": "❤️",
+ "intro": "Romance, dad-style. These love dad jokes have walked someone down the aisle against their will and lived to pun about it.",
+ "keywords": r"\b(love|romantic|valentine|marri(ed|age)|attract|wife|husband|buoy)\b"},
+ {"slug": "birthday", "name": "Birthday Dad Jokes", "emoji": "🎂",
+ "intro": "Because every birthday needs at least one groan with the cake. These birthday dad jokes are tradition now, sorry.",
+ "keywords": r"\b(birthday|cake|candles?|party|presents?)\b"},
+ {"slug": "winter", "name": "Winter Dad Jokes", "emoji": "⛄",
+ "intro": "These winter dad jokes are snow joke. Perfect for breaking the ice at any cold-weather gathering.",
+ "keywords": r"\b(winter|snow|snowman|ic(y|icle)|frost|chill)\b"},
+]
+MIN_TOPIC_JOKES = 5
+
def esc(s):
return html.escape(s, quote=True)
@@ -132,6 +167,10 @@ def page_index():
d_idx = datetime.date.today().toordinal()
jod = JOKES[d_idx % len(JOKES)]
latest = JOKES[-12:][::-1]
+ live_topics = [t for t in TOPICS if len(topic_jokes(t)) >= MIN_TOPIC_JOKES]
+ topic_chips = ''.join('%s %s' % (
+ t['slug'], t['emoji'], esc(t['name'].replace('Dad Jokes About ', '').replace('Dad Jokes', 'Jokes')))
+ for t in live_topics)
cat_cards = ''.join(f"""
{c['emoji']}
@@ -170,6 +209,13 @@ def page_index():
"""
- title = 'Best Dad Jokes — 273+ Funny, Clean Dad Jokes (One-Liners, Puns & Knock-Knocks)'
+ title = 'Best Dad Jokes — %d+ Funny, Clean Dad Jokes (One-Liners, Puns & Knock-Knocks)' % len(JOKES)
desc = ('The best dad jokes on the internet: %d clean, funny, groan-worthy dad jokes. '
'One-liners, knock-knock jokes and puns by category, plus a daily dad joke and a free generator.'
% len(JOKES))
@@ -229,6 +275,34 @@ def page_category(slug):
'' % esc(json.dumps(ld)))
+def topic_jokes(topic):
+ pat = re.compile(topic['keywords'])
+ return [j for j in JOKES if pat.search((j['setup'] + ' ' + j['punch']).lower())]
+
+
+def page_topic(slug):
+ t = next(t for t in TOPICS if t['slug'] == slug)
+ js = topic_jokes(t)
+ if len(js) < MIN_TOPIC_JOKES:
+ return None
+ cards = ''.join(joke_card(j) for j in js)
+ other_topics = [o for o in TOPICS if o['slug'] != slug and len(topic_jokes(o)) >= MIN_TOPIC_JOKES][:6]
+ chips = ''.join('%s %s' % (o['slug'], o['emoji'], esc(o['name'].replace('Dad Jokes About ', '').replace('Dad Jokes', 'Jokes'))) for o in other_topics)
+ body = f"""
+
+
+
{t['emoji']} {esc(t['name'])}
+
{esc(t['intro'])} That's {len(js)} of them, carefully curated for maximum groan.
@@ -428,6 +502,10 @@ def build():
write('index.html', page_index())
for c in CATS:
write('category/%s/index.html' % c['slug'], page_category(c['slug']))
+ for t in TOPICS:
+ page = page_topic(t['slug'])
+ if page:
+ write('topic/%s/index.html' % t['slug'], page)
write('dad-joke-generator/index.html', page_generator())
write('joke-of-the-day/index.html', page_jotd())
write('search/index.html', page_search())
@@ -439,7 +517,8 @@ def build():
# robots + sitemap
pages = ['/', '/dad-joke-generator/', '/joke-of-the-day/', '/search/', '/about/', '/contact/',
- '/privacy-policy/', '/terms/'] + ['/category/%s/' % c['slug'] for c in CATS]
+ '/privacy-policy/', '/terms/'] + ['/category/%s/' % c['slug'] for c in CATS] \
+ + ['/topic/%s/' % t['slug'] for t in TOPICS if len(topic_jokes(t)) >= MIN_TOPIC_JOKES]
write('robots.txt', 'User-agent: *\nAllow: /\n\nSitemap: %s/sitemap.xml\n' % SITE_URL)
today = datetime.date.today().isoformat()
sm = ['',
diff --git a/bdj/content/jokes.json b/bdj/content/jokes.json
index 8c0b75a..6956bff 100644
--- a/bdj/content/jokes.json
+++ b/bdj/content/jokes.json
@@ -301,6 +301,62 @@
{"cat": "one-liners", "type": "line", "setup": "Why can't your nose be 12 inches long?", "punch": "Because then it would be a foot."},
{"cat": "one-liners", "type": "line", "setup": "I'm terrified of elevators.", "punch": "So I'm taking steps to avoid them."},
{"cat": "one-liners", "type": "line", "setup": "Why don't scientists trust atoms?", "punch": "Because they make up everything."},
- {"cat": "one-liners", "type": "line", "setup": "What did the Buddhist monk say to the hot dog vendor?", "punch": "Make me one with everything."}
+ {"cat": "one-liners", "type": "line", "setup": "What did the Buddhist monk say to the hot dog vendor?", "punch": "Make me one with everything."},
+
+ {"cat": "food", "setup": "What do you call a sad cup of coffee?", "punch": "A depres-so."},
+ {"cat": "food", "setup": "How do coffee cups show their love?", "punch": "With mugs and kisses."},
+ {"cat": "food", "setup": "Where do coffee beans go for a night out?", "punch": "The coffee bar."},
+ {"cat": "food", "setup": "Why was the espresso checking its watch?", "punch": "It was pressed for time."},
+ {"cat": "food", "setup": "What's a coffee's favorite magic spell?", "punch": "Espresso Patronum!"},
+ {"cat": "food", "setup": "Why don't coffees ever gossip?", "punch": "They don't want to spill the beans."},
+ {"cat": "food", "setup": "How does coffee show it cares?", "punch": "It gives you a latte attention."},
+
+ {"cat": "animals", "setup": "What do you call a pile of kittens?", "punch": "A meow-ntain."},
+ {"cat": "animals", "setup": "Why don't cats play poker in the jungle?", "punch": "Too many cheetahs."},
+ {"cat": "animals", "setup": "What's a cat's favorite dessert?", "punch": "Mice cream."},
+ {"cat": "animals", "setup": "Why did the cat sit on the computer?", "punch": "To keep an eye on the mouse."},
+ {"cat": "animals", "setup": "What do you call a cat that works at a hospital?", "punch": "A purr-fessional."},
+ {"cat": "animals", "setup": "How do cats end a fight?", "punch": "They hiss and make up."},
+ {"cat": "animals", "setup": "Why is everyone so excited about the cat's new job?", "punch": "Because it's feline good."},
+ {"cat": "animals", "setup": "Why did the dog cross the road?", "punch": "To get to the barking lot."},
+ {"cat": "animals", "setup": "What kind of dog loves surround sound?", "punch": "A subwoofer."},
+ {"cat": "animals", "setup": "Why do dogs run in circles?", "punch": "Because it's too hard to run in squares."},
+ {"cat": "animals", "setup": "What's a dog's favorite instrument?", "punch": "The trom-BONE."},
+ {"cat": "animals", "setup": "How did the puppy feel after finishing its homework?", "punch": "Ruff."},
+ {"cat": "animals", "setup": "Why did the dachshund sit by the fire?", "punch": "It was a cold dog."},
+ {"cat": "animals", "setup": "What breed of dog can jump higher than a house?", "punch": "Any breed — houses can't jump."},
+
+ {"cat": "work", "setup": "Why did the penny get promoted?", "punch": "It made perfect cents."},
+ {"cat": "work", "setup": "What do you call a rich fish?", "punch": "A goldfish."},
+ {"cat": "work", "setup": "Why did the dollar bill break up with the coin?", "punch": "It needed some change."},
+ {"cat": "work", "setup": "What happened to the man who put $20 into a vending machine?", "punch": "Nothing — he's still waiting for his two cents."},
+ {"cat": "work", "setup": "Why was the money exhausted?", "punch": "It was spent."},
+ {"cat": "work", "setup": "Where do cows invest their money?", "punch": "In moo-tual funds."},
+
+ {"cat": "one-liners", "type": "line", "setup": "What's a nap's favorite kind of movie?", "punch": "A sleeper hit."},
+ {"cat": "one-liners", "type": "line", "setup": "Why did the pillow go to the doctor?", "punch": "It was feeling down."},
+ {"cat": "one-liners", "type": "line", "setup": "Why are blankets so good at ending arguments?", "punch": "They know how to wrap things up."},
+ {"cat": "one-liners", "type": "line", "setup": "I'm great at sleeping.", "punch": "I can do it with my eyes closed."},
+ {"cat": "one-liners", "type": "line", "setup": "Why was everyone so tired on the morning of April 1st?", "punch": "They'd just finished a 31-day March."},
+
+ {"cat": "family", "setup": "Why did the two antennas get married?", "punch": "The ceremony was okay, but the reception was excellent."},
+ {"cat": "family", "setup": "What did the paper clip say to the magnet?", "punch": "I find you very attractive."},
+ {"cat": "family", "setup": "What's the most romantic part about the ocean?", "punch": "When the buoy meets gull."},
+ {"cat": "family", "setup": "Kid: 'Do you have a date for Valentine's Day?'", "punch": "Dad: 'Sure — February 14th.'"},
+ {"cat": "family", "setup": "My wife told me to stop impersonating a flamingo.", "punch": "I had to put my foot down."},
+ {"cat": "family", "setup": "Why do candles love birthdays?", "punch": "They get to be the light of the party."},
+ {"cat": "family", "setup": "What did the elephant want for its birthday?", "punch": "A trunk full of presents."},
+ {"cat": "family", "setup": "Why was the birthday cake as hard as a rock?", "punch": "It was marble cake."},
+ {"cat": "family", "setup": "What does a clam do on its birthday?", "punch": "It shellabrates."},
+ {"cat": "family", "setup": "What did the pirate say on his 80th birthday?", "punch": "Aye matey!"},
+
+ {"cat": "food", "setup": "What does an aardvark like on its pizza?", "punch": "Ant-chovies."},
+ {"cat": "food", "setup": "Why did the hipster burn his tongue on his pizza?", "punch": "He ate it before it was cool."},
+ {"cat": "food", "setup": "What kind of pizza do dogs order?", "punch": "Pup-peroni."},
+ {"cat": "food", "setup": "What's the difference between a pizza and my pizza jokes?", "punch": "The pizza can actually feed a family."},
+
+ {"cat": "weather", "setup": "How does a snowman get around town?", "punch": "On an icicle."},
+ {"cat": "weather", "setup": "What did the icy road say to the truck?", "punch": "Want to go for a spin?"},
+ {"cat": "weather", "setup": "How do you prevent a summer cold?", "punch": "Catch it in the winter."}
]
}