commit 47eea16caf90369bb3101e3bb78b101da2b0a5e4 Author: Râu Cao Date: Thu Apr 24 14:23:31 2025 +0400 GM diff --git a/1/README.md b/1/README.md new file mode 100644 index 0000000..42e8088 --- /dev/null +++ b/1/README.md @@ -0,0 +1,49 @@ +# Publish Markdown articles to Nostr + +I'm currently using [this bash script][1] to publish [long-form content][2] +from local Markdown files to Nostr relays. + +It requires all of `yq`, `jq`, and `nak` to be installed. + +## Usage + +Create a signed Nostr event and print it to the console: + + markdown_to_nostr.sh article-filename.md + +Create a Nostr event and publish it to one or more relays: + + markdown_to_nostr.sh article-filename.md ws://localhost:7777 wss://nostr.kosmos.org + +## Markdown format + +You can specify your metadata as YAML in a Front Matter header. Here's an +example file: + +```md +--- +title: "Good Morning" +summary: "It's a beautiful day" +image: https://example.com/i/beautiful-day.jpg +date: 2025-04-24T15:00:00Z +tags: gm, poetry +published: false +--- + +In the blue sky just a few specks of gray +In the evening of a beautiful day +Though last night it rained and more rain on the way +And that more rain is needed 'twould be fair to say. + +— Francis Duggan +``` + +The metadata keys are mostly self-explanatory. Note: + +* All keys except for `title` are optional +* `date`, if present, will be set as the `published_at` date. +* If `published` is set to `true`, it will publish a kind 30023 event, + otherwise a kind 30024 (draft) + +[1]: https://gitea.kosmos.org/raucao/gists/src/branch/master/1/markdown_to_nostr.sh +[2]: https://github.com/nostr-protocol/nips/blob/master/23.md diff --git a/1/markdown_to_nostr.sh b/1/markdown_to_nostr.sh new file mode 100755 index 0000000..d7c5296 --- /dev/null +++ b/1/markdown_to_nostr.sh @@ -0,0 +1,176 @@ +#!/bin/bash + +# Check if yq, jq, and nak are installed +if ! command -v yq &> /dev/null; then + echo "Error: yq is not installed. Please install yq to proceed." + exit 1 +fi +if ! command -v jq &> /dev/null; then + echo "Error: jq is not installed. Please install jq to proceed." + exit 1 +fi +if ! command -v nak &> /dev/null; then + echo "Error: nak is not installed. Please install nak to proceed." + exit 1 +fi + +# Check if at least one input file is provided +if [ $# -lt 1 ]; then + echo "Usage: $0 [relay...]" + echo "Converts a Markdown file with front matter to a Nostr event (kind 30023 if published: true, else kind 30024), prompts for secret key, and optionally publishes to relays." + exit 1 +fi + +MD_FILE="$1" +shift # Remove MD_FILE from arguments, leaving relays (if any) + +# Check if file exists and is a Markdown file +if [ ! -f "$MD_FILE" ] || [[ ! "$MD_FILE" =~ \.md$ ]]; then + echo "Error: '$MD_FILE' is not a valid .md file" + exit 1 +fi + +# Extract filename without .md extension for d tag +D_TAG=$(basename "$MD_FILE" .md) + +# Extract front matter (between --- delimiters) +FRONT_MATTER=$(awk ' + BEGIN { in_front_matter=0; front_matter_count=0 } + /^---$/ { + front_matter_count++ + if (front_matter_count == 1) { in_front_matter=1; next } + else if (front_matter_count == 2) { in_front_matter=0; exit } + } + in_front_matter { print } +' "$MD_FILE") + +# Check if front matter is empty or invalid +if [ -z "$FRONT_MATTER" ]; then + echo "Error: '$MD_FILE' has no valid front matter" + exit 1 +fi + +# Parse front matter with yq using a temporary file +echo "$FRONT_MATTER" > .temp_front_matter.yaml +FRONT_MATTER_JSON=$(yq -r '.' .temp_front_matter.yaml 2>/dev/null) +rm .temp_front_matter.yaml + +if [ $? -ne 0 ] || [ -z "$FRONT_MATTER_JSON" ]; then + echo "Error: '$MD_FILE' has invalid YAML front matter" + exit 1 +fi + +# Extract title, summary, date, tags, image, and published from front matter +TITLE=$(echo "$FRONT_MATTER_JSON" | jq -r '.title // ""') +SUMMARY=$(echo "$FRONT_MATTER_JSON" | jq -r '.summary // ""') +DATE=$(echo "$FRONT_MATTER_JSON" | jq -r '.date // ""') +TAGS=$(echo "$FRONT_MATTER_JSON" | jq -r '.tags // ""') +IMAGE=$(echo "$FRONT_MATTER_JSON" | jq -r '.image // ""') +PUBLISHED=$(echo "$FRONT_MATTER_JSON" | jq -r '.published // "false"') + +# Convert ISO 8601 date to Unix timestamp for published_at (if valid) +if [ -n "$DATE" ]; then + PUBLISHED_AT=$(date -d "$DATE" +%s 2>/dev/null || echo "$DATE") +else + PUBLISHED_AT="" +fi + +# Extract content (everything after front matter) and trim only leading/trailing blank lines +CONTENT=$(awk ' + BEGIN { in_front_matter=0; front_matter_count=0; content_started=0 } + /^---$/ { + front_matter_count++ + if (front_matter_count == 1) { in_front_matter=1 } + else if (front_matter_count == 2) { in_front_matter=0; next } + } + !in_front_matter && front_matter_count >= 2 { + if (!content_started && $0 ~ /^[[:space:]]*$/) { + next # Skip leading blank lines + } + content_started=1 + content = content $0 "\n" + } + END { + # Trim trailing blank lines + if (content != "") { + sub(/\n*[[:space:]]*\n*$/, "", content) + print content + } + } +' "$MD_FILE") + +# Check if content is empty +if [ -z "$CONTENT" ]; then + echo "Error: No content found after front matter in '$MD_FILE'" + exit 1 +fi + +# Write content to a temporary file to avoid escaping issues +TEMP_CONTENT=$(mktemp) +echo -n "$CONTENT" > "$TEMP_CONTENT" + +# Determine event kind based on published status +if [ "$PUBLISHED" = "true" ]; then + EVENT_KIND=30023 +else + EVENT_KIND=30024 +fi + +# Build nak command with --prompt-sec +NAK_CMD="nak event --kind $EVENT_KIND --content \"\$(cat $TEMP_CONTENT)\" --prompt-sec" + +# Add tags +NAK_CMD="$NAK_CMD -d \"$D_TAG\"" +if [ -n "$TITLE" ]; then + NAK_CMD="$NAK_CMD --tag title=\"$TITLE\"" +fi +if [ -n "$SUMMARY" ]; then + NAK_CMD="$NAK_CMD --tag summary=\"$SUMMARY\"" +fi +if [ -n "$PUBLISHED_AT" ]; then + NAK_CMD="$NAK_CMD --tag published_at=\"$PUBLISHED_AT\"" +fi +if [ -n "$IMAGE" ]; then + NAK_CMD="$NAK_CMD --tag image=\"$IMAGE\"" +fi + +# Add t tags from comma-separated front matter tags +if [ -n "$TAGS" ]; then + # Split comma-separated tags, trim whitespace, and add as t tags + IFS=',' read -ra TAG_ARRAY <<< "$TAGS" + for TAG in "${TAG_ARRAY[@]}"; do + # Trim leading/trailing whitespace from tag + TAG=$(echo "$TAG" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//') + if [ -n "$TAG" ]; then + NAK_CMD="$NAK_CMD --tag t=\"$TAG\"" + fi + done +fi + +# Add relays (if provided) +for RELAY in "$@"; do + NAK_CMD="$NAK_CMD \"$RELAY\"" +done + +# Create a temporary file for nak output +TEMP_OUTPUT=$(mktemp) + +# Execute nak command, directing prompt to terminal and capturing output +# Use /dev/tty for stdin to ensure prompt is interactive +eval "$NAK_CMD > $TEMP_OUTPUT" < /dev/tty +NAK_EXIT=$? + +# Read the output from the temporary file +EVENT_JSON=$(cat "$TEMP_OUTPUT") + +# Clean up temporary files +rm "$TEMP_CONTENT" "$TEMP_OUTPUT" + +# Check if nak command succeeded +if [ $NAK_EXIT -ne 0 ] || [ -z "$EVENT_JSON" ]; then + echo "Error: Failed to create Nostr event with nak (check secret key input)" + exit 1 +fi + +# Output the JSON object on a single line +echo "$EVENT_JSON" | jq -c .