216 lines
4.6 KiB
Python
Executable File
216 lines
4.6 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):
|
|
"""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)
|
|
|
|
content = f'''---
|
|
title: '{title}'
|
|
date: {now}
|
|
draft: true
|
|
series: "Fun Center"
|
|
summary: "{summary}"
|
|
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()
|
|
|
|
# Create the post
|
|
filepath = create_post(title, post_type, tags, summary)
|
|
|
|
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()
|