#!/usr/bin/env python3
"""Static generator for helpme.tips — arbitrage-ready content site.
Articles are multi-page (pagination = more ad impressions per session).
Python 3.6 stdlib only. Ad slots are emitted as comments
plus empty mount divs; the fleet ad wrapper fills them.
"""
import json, os, shutil, html, datetime
ROOT = os.path.dirname(os.path.abspath(__file__))
SITE_DIR = os.path.join(ROOT, 'site')
STATIC = os.path.join(ROOT, 'static')
SITE_URL = 'https://helpme.tips'
SITE_NAME = 'Help Me Tips'
YEAR = datetime.date.today().year
def esc(s):
return html.escape(s, quote=True)
def load_articles():
with open(os.path.join(ROOT, 'content', 'articles.json')) as f:
arts = json.load(f)
for i, a in enumerate(arts):
a['id'] = i
return arts
def layout(title, desc, body, path='/', extra_head=''):
return f"""
Short, practical guides for everyday problems — kitchen, sleep, coffee, habits, home and your phone. No fluff, no 800-word intros.
Latest tips
{cards}
Why Help Me Tips?
Most tip articles online bury one useful sentence under a wall of filler. We do the opposite: every page is a tight, actionable list you can finish with your coffee. New guides are added every week across home, kitchen, sleep, tech and habits.
"""
title = 'Help Me Tips — Practical Everyday Tips That Actually Help'
desc = ('Short, practical guides for everyday life: kitchen shortcuts, better sleep, '
'coffee, phone settings, habits and home. New tips weekly.')
return layout(title, desc, body, '/')
def page_article(a, page_idx, total):
pg = a['pages'][page_idx]
parts = []
if pg.get('heading'):
parts.append('
%s
' % esc(pg['heading']))
parts += ['
%s
' % esc(p) for p in pg['paras']]
for h, paras in pg.get('sections', []):
parts.append('
%s
' % esc(h))
parts += ['
%s
' % esc(p) for p in paras]
# multi-item list support
content = '\n'.join(parts)
nav = []
if page_idx > 0:
prev = '/' if page_idx == 1 else '/tips/%s/%d/' % (a['slug'], page_idx)
nav.append('← Previous' % prev)
nav.append('Page %d of %d' % (page_idx + 1, total))
if page_idx < total - 1:
nav.append('Next page →' % (a['slug'], page_idx + 2))
nav_html = '
%s
' % '\n'.join(nav)
dots = '
%s
' % ''.join(
'' % ('on' if i == page_idx else '') for i in range(total))
body = f"""
{esc(a['title'])}
{esc(a['dek'])}
{a.get('updated', '')} · Page {page_idx + 1} of {total} · {a.get('read_min', 4)} min read
{content}
{dots}
{nav_html}
Keep reading
{more_cards(a, articles_all)}
"""
suffix = '' if page_idx == 0 else ' (Page %d)' % (page_idx + 1)
title = '%s%s — Help Me Tips' % (a['title'], suffix)
desc = a['dek']
ld = {"@context": "https://schema.org", "@type": "Article", "headline": a['title'],
"description": a['dek'], "mainEntityOfPage": SITE_URL + '/tips/%s/' % a['slug'],
"datePublished": a.get('updated', '2026-09-16')}
extra = '' % esc(json.dumps(ld))
path = '/tips/%s/' % a['slug'] if page_idx == 0 else '/tips/%s/%d/' % (a['slug'], page_idx + 1)
return layout(title, desc, body, path, extra)
def more_cards(current, all_arts):
others = [a for a in all_arts if a['slug'] != current['slug']][:4]
return ''.join(article_card(a) for a in others)
articles_all = [] # populated in build(); used by more_cards
def page_about():
body = """
About Help Me Tips
Help Me Tips publishes short, practical guides for everyday problems. We believe a good tip article should respect your time: clear steps, no 800-word personal essays, no product pitches.
Topics we cover: kitchen and cooking shortcuts, sleep quality, coffee, phone and tech settings, home care, and small habits that compound.
Our editorial standard
Every tip must be actionable today with things you already own.
No medical, legal or financial claims. General information only.
If a tip doesn't survive being tested in a real home, it doesn't ship.
"""
return layout('About — Help Me Tips', 'What Help Me Tips is and how we pick tips.', body, '/about/')
def page_contact():
body = """
Contact
Tip ideas, corrections, feedback? We read everything: hello@helpme.tips
"""
return layout('Contact — Help Me Tips', 'Contact Help Me Tips.', body, '/contact/')
def page_privacy():
body = """
Privacy Policy
Last updated: %s
Help Me Tips collects standard web server logs (IP address, browser type, pages visited) for security and aggregate statistics. We do not require accounts and do not collect names or emails.
Cookies and advertising
We display advertising to keep this site 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. You may opt out of personalized advertising at Google Ads Settings or aboutads.info.
Where required by law (EEA/UK), a consent banner will let you accept or reject non-essential cookies, and your choice will be honored.
Data sharing
We do not sell personal information. Analytics, if used, are aggregate and not identifiable.
""" % datetime.date.today().strftime('%B %d, %Y')
return layout('Privacy Policy — Help Me Tips', 'Privacy policy for helpme.tips.', body, '/privacy-policy/')
def page_terms():
body = """
Terms of Use
Last updated: %s
By using helpme.tips you agree to these terms. Content is provided for general information only and is not professional, medical, legal or financial advice. Use tips at your own discretion.
Use of content
Personal, non-commercial use only. Bulk republication requires written permission.
No warranties
The site is provided "as is" without warranties of any kind. Results of following tips may vary.
""" % datetime.date.today().strftime('%B %d, %Y')
return layout('Terms of Use — Help Me Tips', 'Terms for helpme.tips.', body, '/terms/')
def page_404():
body = """
"""
return layout('404 — Help Me Tips', 'Page not found.', body, '/404.html')
def write(path, content):
full = os.path.join(SITE_DIR, path)
os.makedirs(os.path.dirname(full) or SITE_DIR, exist_ok=True)
with open(full, 'w') as f:
f.write(content)
def build():
global articles_all
if os.path.exists(SITE_DIR):
shutil.rmtree(SITE_DIR)
os.makedirs(SITE_DIR)
st = os.path.join(SITE_DIR, 'static')
os.makedirs(st, exist_ok=True)
for fn in os.listdir(STATIC):
shutil.copy(os.path.join(STATIC, fn), st)
articles = load_articles()
articles_all = articles
articles.sort(key=lambda a: a.get('updated', ''), reverse=True)
for a in articles:
a['pages_count'] = len(a['pages'])
write('index.html', page_index(articles))
n_pages = 0
for a in articles:
total = len(a['pages'])
a['pages_count'] = total
for i in range(total):
write('tips/%s/index.html' % a['slug'] if i == 0
else 'tips/%s/%d/index.html' % (a['slug'], i + 1),
page_article(a, i, total))
n_pages += 1
write('about/index.html', page_about())
write('contact/index.html', page_contact())
write('privacy-policy/index.html', page_privacy())
write('terms/index.html', page_terms())
write('404.html', page_404())
pages = ['/', '/about/', '/contact/', '/privacy-policy/', '/terms/'] + \
['/tips/%s/' % a['slug'] for a in articles]
today = datetime.date.today().isoformat()
sm = ['',
'']
for p in pages:
sm.append('%s%s%s' % (SITE_URL, p, today))
sm.append('')
write('sitemap.xml', '\n'.join(sm))
write('robots.txt', 'User-agent: *\nAllow: /\n\nSitemap: %s/sitemap.xml\n' % SITE_URL)
nfiles = sum(len(fn) for _, _, fn in os.walk(SITE_DIR))
total_bytes = sum(os.path.getsize(os.path.join(dp, f)) for dp, _, fn in os.walk(SITE_DIR) for f in fn)
print('Built %d files (%d article pages, %d articles) -> %s (%d bytes)'
% (nfiles, n_pages, len(articles), SITE_DIR, total_bytes))
if __name__ == '__main__':
build()