#!/usr/bin/env python3
"""Static site generator for bestdadjokes.lol. Python 3.6 stdlib only."""
import json, os, re, shutil, html, datetime
ROOT = os.path.dirname(os.path.abspath(__file__))
CONTENT = os.path.join(ROOT, 'content', 'jokes.json')
SITE_DIR = os.path.join(ROOT, 'site')
STATIC = os.path.join(ROOT, 'static')
SITE_URL = 'https://bestdadjokes.lol'
SITE_NAME = 'Best Dad Jokes'
TAGLINE = 'The best dad jokes on the internet โ short, clean, and groan-worthy.'
YEAR = datetime.date.today().year
with open(CONTENT) as f:
DATA = json.load(f)
CATS = DATA['categories']
JOKES = DATA['jokes']
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)
def joke_count(slug):
return sum(1 for j in JOKES if j['cat'] == slug)
def joke_card(j, reveal=False):
"""A single joke card. Punchline is in the DOM (SEO-safe); JS blurs until clicked."""
cls = 'joke-card'
if j.get('type') == 'knock':
body = '
%s
' % esc(j['setup']).replace('\n', ' ')
else:
body = '
%s
' % esc(j['setup'])
punch = '
%s
' % esc(j['punch'])
share = (''
'' % (j['id'], j['id']))
return ('%s%s'
'%s'
% (cls, j['id'], body, punch, share))
def layout(title, desc, body, path='/', extra_head=''):
nav_cats = ''.join(
'%s %s' % (c['slug'], c['emoji'], esc(c['name']))
for c in CATS[:7])
rest = ''.join('%s %s' % (c['slug'], c['emoji'], esc(c['name']))
for c in CATS[7:])
foot_cats = ''.join('%s' % (c['slug'], esc(c['name'])) for c in CATS)
return f"""
{esc(title)}
{extra_head}
A dad joke is a short, clean, pun-based joke delivered with maximum confidence and zero shame. It's the kind of joke that makes your kids groan, your wife roll her eyes, and your dog leave the room. Science has yet to explain why hearing one makes you feel briefly smarter and visibly worse at the same time.
This site is a growing collection of the best dad jokes on the internet: one-liners, knock-knock jokes, and puns about animals, food, work, school, sports, tech, space and more. Every joke is family-friendly โ the only thing at risk of injury is everyone's patience. Start with the dad joke generator, browse the categories, or come back every day for a new dad joke of the day.
"""
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))
ld = {
"@context": "https://schema.org",
"@type": "WebSite",
"name": SITE_NAME,
"url": SITE_URL + '/',
"description": TAGLINE,
"potentialAction": {
"@type": "SearchAction",
"target": SITE_URL + "/search/?q={search_term_string}",
"query-input": "required name=search_term_string"
}
}
return layout(title, desc, body, '/',
'' % esc(json.dumps(ld)))
def page_category(slug):
c = CAT_BY_SLUG[slug]
js = [j for j in JOKES if j['cat'] == slug]
cards = ''.join(joke_card(j) for j in js)
others = [o for o in CATS if o['slug'] != slug][:6]
others_html = ''.join('%s %s' % (o['slug'], o['emoji'], esc(o['name'])) for o in others)
body = f"""
{c['emoji']} {esc(c['name'])}
{esc(c['blurb'])} That's {len(js)} jokes of pure dad energy.
"""
title = 'Dad Joke Generator โ Random Dad Jokes, One Click'
desc = 'Press one button and get a random dad joke instantly. Hundreds of clean, funny dad jokes ready to embarrass your kids.'
return layout(title, desc, body, '/dad-joke-generator/')
def page_jotd():
d = datetime.date.today()
j = JOKES[d.toordinal() % len(JOKES)]
body = f"""
๐ Dad Joke of the Day
A brand-new dad joke every single day. Doctors recommend exactly one per day. We won't tell if you don't.
{d.strftime('%B %d, %Y')}
{esc(j['setup'])}
{esc(j['punch'])}
The dad joke archive
Every recent dad joke of the day, one per day, no repeats (for a few months, anyway):
"""
title = 'Dad Joke of the Day โ A New Funny Dad Joke Daily'
desc = "Today's dad joke of the day, plus the full archive. One clean, funny dad joke for every day of the year."
ld = {"@context": "https://schema.org", "@type": "SpecialAnnouncement", "name": "Dad Joke of the Day",
"url": SITE_URL + '/joke-of-the-day/', "category": "https://schema.org/EntertainmentEvent"}
return layout(title, desc, body, '/joke-of-the-day/',
'' % SITE_URL +
'' % esc(json.dumps(ld)))
def page_search():
body = """
๐ Search Dad Jokes
Looking for a dad joke about coffee, cats, or cardio? Type it in. We probably have a pun for that.
No jokes matched. Which, honestly, is a great setup for a new dad joke โ send it to us via the contact page.
"""
title = 'Search Dad Jokes โ Find a Dad Joke About Anything'
desc = 'Search hundreds of clean dad jokes by keyword. Find puns and one-liners about any topic instantly.'
return layout(title, desc, body, '/search/')
def page_about():
body = """
๐ About Best Dad Jokes
Welcome to BestDadJokes.lol โ a growing collection of the internet's best dad jokes: short, clean, pun-heavy, and delivered with the quiet confidence only a dad can muster.
The mission is simple: build the most useful dad joke resource on the internet. That means:
Curated, not scraped. Every joke is hand-picked for maximum groan. If it doesn't make someone audibly sigh, it doesn't make the cut.
Family-friendly, always. Clean humor that's safe to tell at the dinner table, the office, or the school pickup line.
Fast and simple. No pop-ups, no logins, no nonsense. Just jokes.
Why dad jokes?
Because a dad joke is never just a joke. It's a love language. It's saying "I would rather embarrass myself than miss a chance to make you laugh." Also, they're free, and this is a budget operation.
Get in touch
Got a joke that deserves to be here? Found a bug? Want to license 300 dad jokes for your family group chat? Head to the contact page.
"""
title = 'About โ Best Dad Jokes .lol'
desc = 'BestDadJokes.lol is a curated collection of clean, funny dad jokes. Hand-picked jokes, family-friendly humor, zero nonsense.'
return layout(title, desc, body, '/about/')
def page_contact():
body = """
๐ฎ Contact
Want to send us the greatest dad joke of all time? Report a joke that's more "dad" than "joke"? Tell us the site made you laugh so hard you spilled your coffee? We read everything.
Include the setup and the punchline in your email. If we use it, you get full bragging rights and absolutely no money, which is exactly how dad economics works.
"""
title = 'Contact โ Best Dad Jokes .lol'
desc = 'Get in touch with BestDadJokes.lol โ submit a dad joke, say hi, or report a bug.'
return layout(title, desc, body, '/contact/')
def page_privacy():
body = """
Privacy Policy
Last updated: %s
BestDadJokes.lol respects your privacy, which is ironic because dads famously respect nothing more than a good interruption. Here's the deal in plain English:
What we collect
We keep standard web server logs (IP address, browser type, pages visited) for security and aggregate traffic statistics. We do not ask for your name, email, or your kids' report cards. There are no accounts on this site.
Cookies and advertising
We may display advertising on this site to keep it free. Advertising partners, which may include Google, use cookies and similar technologies to serve ads based on your prior visits to this and other websites.
Google's use of advertising cookies enables it and its partners to serve ads based on your visits to this site and/or other sites on the Internet. You may opt out of personalized advertising by visiting Google Ads Settings, or opt out of third-party vendor cookies at aboutads.info.
If we run a consent banner for visitors from the EEA/UK, your choices will be honored and stored locally.
Analytics
We use aggregate, privacy-respecting analytics to count page views and understand which categories people enjoy. This data cannot reasonably be used to identify you.
Data sharing
We do not sell personal data. We have no personal data to sell. We barely have data.
Children
Content on this site is family-friendly by design. The site is not directed at children under 13, and we do not knowingly collect personal information from children.
""" % datetime.date.today().strftime('%B %d, %Y')
title = 'Privacy Policy โ Best Dad Jokes .lol'
desc = 'Privacy policy for BestDadJokes.lol โ what we collect (very little), cookies, advertising, and your choices.'
return layout(title, desc, body, '/privacy-policy/')
def page_terms():
body = """
Terms of Use
Last updated: %s
By using BestDadJokes.lol you agree to the following terms. Reading them aloud in a dad voice is optional but encouraged.
Use of content
The jokes on this site are provided for personal, non-commercial enjoyment. Share them freely at dinner tables, offices, and group chats. If you want to republish jokes in bulk or use this site commercially, contact us first โ we're reasonable people.
No warranties
This site is provided "as is." We make no guarantees that jokes will be funny, that groans will be produced, or that your teenagers will acknowledge your existence. All comedic risk is yours.
Limitation of liability
To the fullest extent permitted by law, BestDadJokes.lol is not liable for any damages arising from use of the site, including but not limited to eye-rolling, sighing, involuntary chuckling, and children hiding your phone.
External links
We are not responsible for the content of external websites, including their jokes, which are almost certainly worse than ours.
Changes
We may update these terms occasionally. Continued use of the site means you accept the updated terms, and possibly a fresh dad joke.
""" % datetime.date.today().strftime('%B %d, %Y')
title = 'Terms of Use โ Best Dad Jokes .lol'
desc = 'Terms of use for BestDadJokes.lol.'
return layout(title, desc, body, '/terms/')
def page_404():
body = """
404
This page is like a dad joke that got cut from the site โ it just didn't land.