All three post creation scripts now ask whether to publish to gopher phlog and include the phlog field in generated frontmatter.
222 lines
4.8 KiB
Python
Executable File
222 lines
4.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Create a new Fun Center (technology) blog post.
|
|
|
|
Usage:
|
|
python scripts/new_techpost.py
|
|
python scripts/new_techpost.py "My Post Title"
|
|
"""
|
|
|
|
import argparse
|
|
import re
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
# Paths
|
|
SCRIPT_DIR = Path(__file__).parent
|
|
PROJECT_ROOT = SCRIPT_DIR.parent
|
|
CONTENT_DIR = PROJECT_ROOT / "content" / "posts"
|
|
|
|
# Common tags for tech posts
|
|
COMMON_TAGS = [
|
|
"linux",
|
|
"ubuntu",
|
|
"self-hosted",
|
|
"open-source",
|
|
"privacy",
|
|
"automation",
|
|
"devops",
|
|
"python",
|
|
"homelab",
|
|
"sdf",
|
|
"hugo",
|
|
]
|
|
|
|
|
|
def slugify(title):
|
|
"""Convert title to URL-friendly slug."""
|
|
slug = title.lower()
|
|
slug = re.sub(r"[^a-z0-9\s-]", "", slug)
|
|
slug = re.sub(r"[\s_]+", "-", slug)
|
|
slug = re.sub(r"-+", "-", slug)
|
|
return slug.strip("-")
|
|
|
|
|
|
def prompt_post_type():
|
|
"""Ask user what type of post this is."""
|
|
print("\nWhat kind of post is this?")
|
|
print(" 1. How I Did It - Problem/solution, troubleshooting, tutorials")
|
|
print(" 2. Grinds My Gears - Opinion, rant, commentary")
|
|
print(" 3. Quick Tip - Short discovery, TIL, neat trick")
|
|
|
|
while True:
|
|
choice = input("Enter 1, 2, or 3: ").strip()
|
|
if choice == "1":
|
|
return "how-to"
|
|
elif choice == "2":
|
|
return "opinion"
|
|
elif choice == "3":
|
|
return "quick-tip"
|
|
else:
|
|
print("Please enter 1, 2, or 3")
|
|
|
|
|
|
def prompt_tags(post_type):
|
|
"""Suggest and collect tags."""
|
|
# Suggest tags based on post type
|
|
type_tags = {
|
|
"how-to": ["how-i-did-it", "technology"],
|
|
"opinion": ["opinion", "technology"],
|
|
"quick-tip": ["til", "technology"],
|
|
}
|
|
|
|
suggested = type_tags.get(post_type, ["technology"])
|
|
|
|
print(f"\nSuggested tags: {', '.join(suggested)}")
|
|
print(f"Common tags: {', '.join(COMMON_TAGS)}")
|
|
|
|
additional = input("Additional tags (comma-separated, or Enter to skip): ").strip()
|
|
|
|
tags = suggested.copy()
|
|
if additional:
|
|
for tag in additional.split(","):
|
|
tag = tag.strip().lower().replace(" ", "-")
|
|
if tag and tag not in tags:
|
|
tags.append(tag)
|
|
|
|
return tags
|
|
|
|
|
|
def get_skeleton(post_type):
|
|
"""Get the content skeleton based on post type."""
|
|
if post_type == "how-to":
|
|
return """
|
|
## The Problem
|
|
|
|
What broke? What were you trying to do?
|
|
|
|
|
|
## What I Tried
|
|
|
|
The journey - commands, dead ends, frustration...
|
|
|
|
|
|
## What Worked
|
|
|
|
The fix! Include the commands/steps.
|
|
|
|
|
|
## Hindsight
|
|
|
|
What would you do differently? Any gotchas for future-you?
|
|
|
|
"""
|
|
elif post_type == "opinion":
|
|
return """
|
|
## The Thing
|
|
|
|
What's on your mind?
|
|
|
|
|
|
## Why It Matters
|
|
|
|
Some context...
|
|
|
|
|
|
## My Take
|
|
|
|
Your opinion here...
|
|
|
|
|
|
## Links
|
|
|
|
- [Reference 1](https://example.com)
|
|
|
|
"""
|
|
else: # quick-tip
|
|
return """
|
|
Quick tip or discovery here. Keep it short!
|
|
|
|
```bash
|
|
# command or code if relevant
|
|
```
|
|
|
|
"""
|
|
|
|
|
|
def create_post(title, post_type, tags, summary, phlog=False):
|
|
"""Create the post file."""
|
|
CONTENT_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
slug = slugify(title)
|
|
filename = f"{slug}.md"
|
|
filepath = CONTENT_DIR / filename
|
|
|
|
if filepath.exists():
|
|
print(f"\nPost already exists: {filepath.relative_to(PROJECT_ROOT)}")
|
|
overwrite = input("Overwrite? (y/N): ").strip().lower()
|
|
if overwrite != "y":
|
|
return None
|
|
|
|
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
tags_yaml = "\n".join(f" - {tag}" for tag in tags)
|
|
skeleton = get_skeleton(post_type)
|
|
phlog_str = "true" if phlog else "false"
|
|
|
|
content = f'''---
|
|
title: '{title}'
|
|
date: {now}
|
|
draft: true
|
|
series: "Fun Center"
|
|
summary: "{summary}"
|
|
phlog: {phlog_str}
|
|
tags:
|
|
{tags_yaml}
|
|
---
|
|
{skeleton}'''
|
|
|
|
filepath.write_text(content)
|
|
return filepath
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Create a new Fun Center tech post")
|
|
parser.add_argument("title", nargs="?", help="Post title")
|
|
args = parser.parse_args()
|
|
|
|
# Get title
|
|
if args.title:
|
|
title = args.title
|
|
else:
|
|
title = input("Post title: ").strip()
|
|
if not title:
|
|
print("Title is required")
|
|
sys.exit(1)
|
|
|
|
# Get post type
|
|
post_type = prompt_post_type()
|
|
|
|
# Get tags
|
|
tags = prompt_tags(post_type)
|
|
|
|
# Get summary
|
|
summary = input("\nOne-line summary: ").strip()
|
|
|
|
# Ask about gopher phlog
|
|
phlog_input = input("\nPublish to gopher phlog? (y/N): ").strip().lower()
|
|
phlog = phlog_input == "y"
|
|
|
|
# Create the post
|
|
filepath = create_post(title, post_type, tags, summary, phlog)
|
|
|
|
if filepath:
|
|
print(f"\nCreated: {filepath.relative_to(PROJECT_ROOT)}")
|
|
print(f"Type: {post_type}")
|
|
print(f"Tags: {', '.join(tags)}")
|
|
print("\nEdit the file to fill in the skeleton!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|