1
0
This commit is contained in:
Râu Cao 2025-04-24 14:23:31 +04:00
commit 47eea16caf
Signed by: raucao
GPG Key ID: 37036C356E56CC51
2 changed files with 225 additions and 0 deletions

49
1/README.md Normal file
View File

@ -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

176
1/markdown_to_nostr.sh Executable file
View File

@ -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 <markdown_file.md> [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 .