wordpress-rest-curl/post.sh
2026-07-30 19:09:50 +02:00

165 lines
5.4 KiB
Bash
Executable File

#!/bin/bash
# WordPress posting script
# config:
EDITOR=vim
TRANSFORM=() # empty or a subset of: 'title' 'markdown'
USER="" # WP user to create the post
PASSWORD="" # application password generated for your WP user
SERVER="" # server hostname, optionally with subdirectories
STATUS="draft" # one of publish,future,draft,pending,private
CATEGORIES="" # comma separated integer IDs of categories
TAGS="" # comma separated integer IDs of tags
TMPFILE=/tmp/wordpress-post.txt # location of a temporary file with the post text
USER_AGENT="Mozilla/5.0 (X11; Linux x86_64) wordpress-rest-curl/1.0" # some hosts block curl's default UA as bot traffic
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/lib/config.sh"
# CLI options:
# --site NAME after config.sh, also source $CONFIG_DIR/NAME.sh, whose
# values override config.sh's (lets you keep just the
# per-instance bits — USER/PASSWORD/SERVER — in each site
# file, and shared defaults in config.sh)
# --edit POST_ID update an existing post instead of creating a new one
# --publish shortcut for STATUS=publish
# --file PATH use PATH as the post source instead of opening $EDITOR
# --date DATE backdate the post to DATE, an ISO 8601 UTC timestamp
# (e.g. 2026-07-27T10:00:00Z, matching a Gitea commit
# date) — sent as the post's date_gmt
# --category NAME attach category NAME, looking up its ID or creating it
# if it doesn't exist yet (repeatable)
# --tag NAME same as --category, for tags (repeatable)
SITE=""
POST_ID=""
SOURCE_FILE=""
PUBLISH=0
DATE=""
CATEGORY_NAMES=()
TAG_NAMES=()
while [[ $# -gt 0 ]]; do
case "$1" in
--site)
SITE="$2"; shift 2 ;;
--edit)
POST_ID="$2"; shift 2 ;;
--publish)
PUBLISH=1; shift ;;
--file)
SOURCE_FILE="$2"; shift 2 ;;
--date)
DATE="$2"; shift 2 ;;
--category)
CATEGORY_NAMES+=("$2"); shift 2 ;;
--tag)
TAG_NAMES+=("$2"); shift 2 ;;
*)
echo "Unknown argument: $1" >&2
exit 1 ;;
esac
done
wp_load_config
[[ "$PUBLISH" -eq 1 ]] && STATUS="publish"
# let the user provide the post content
if [[ -n "$SOURCE_FILE" ]]; then
cp "$SOURCE_FILE" "$TMPFILE" || exit 1
else
$EDITOR $TMPFILE || exit 1
fi
[[ -e $TMPFILE ]] || exit 1
# transformations
cp $TMPFILE $TMPFILE.trans
for T in "${TRANSFORM[@]}"; do
if [[ "$T" == "title" ]]; then
python3 >$TMPFILE.trans2 <<EOF
import re
with open('$TMPFILE.trans', 'r') as file:
title = ''
content = file.read()
matches = re.match('^#+ (.+)', content, flags=0)
if matches:
title = matches.group(1)
content = content[len(matches.group(0)) + 1:]
with open('$TMPFILE.title', 'w') as titlefile:
titlefile.write(title)
print(content)
EOF
TITLE=`cat $TMPFILE.title`
rm $TMPFILE.title
mv $TMPFILE.trans2 $TMPFILE.trans
elif [[ "$T" == "markdown" ]]; then
python3 >$TMPFILE.trans2 <<EOF
import markdown2
print(markdown2.markdown_path('$TMPFILE.trans'))
EOF
mv $TMPFILE.trans2 $TMPFILE.trans
fi
done
CONTENT=`cat $TMPFILE.trans`
rm $TMPFILE.trans
echo "--- START POST ---"
echo "$CONTENT"
echo "--- END POST ---"
echo "Title: $TITLE"
echo "User: $USER"
echo "Server: $SERVER"
echo "Status: $STATUS"
CATEGORIES_DISPLAY="$CATEGORIES"
[[ ${#CATEGORY_NAMES[@]} -gt 0 ]] && CATEGORIES_DISPLAY="$CATEGORIES_DISPLAY (+ ${CATEGORY_NAMES[*]}, resolved/created on confirm)"
echo "Categories: $CATEGORIES_DISPLAY"
TAGS_DISPLAY="$TAGS"
[[ ${#TAG_NAMES[@]} -gt 0 ]] && TAGS_DISPLAY="$TAGS_DISPLAY (+ ${TAG_NAMES[*]}, resolved/created on confirm)"
echo "Tags: $TAGS_DISPLAY"
if [[ -n "$DATE" ]]; then
echo "Date: $DATE"
fi
if [[ -n "$POST_ID" ]]; then
echo "Editing post: $POST_ID"
fi
echo
read -p "Press enter to confirm..."
# build the JSON payload (categories/tags as real arrays, not comma strings)
CATEGORIES_JSON="[]"
if [[ -n "$CATEGORIES" ]]; then
CATEGORIES_JSON=$(echo "$CATEGORIES" | tr ',' '\n' | jq -R 'select(length > 0) | tonumber' | jq -s '.')
fi
for NAME in "${CATEGORY_NAMES[@]}"; do
ID=$(wp_resolve_term "/wp-json/wp/v2/categories" "$NAME") || exit 1
CATEGORIES_JSON=$(jq -c --argjson id "$ID" '. + [$id] | unique' <<<"$CATEGORIES_JSON")
done
TAGS_JSON="[]"
if [[ -n "$TAGS" ]]; then
TAGS_JSON=$(echo "$TAGS" | tr ',' '\n' | jq -R 'select(length > 0) | tonumber' | jq -s '.')
fi
for NAME in "${TAG_NAMES[@]}"; do
ID=$(wp_resolve_term "/wp-json/wp/v2/tags" "$NAME") || exit 1
TAGS_JSON=$(jq -c --argjson id "$ID" '. + [$id] | unique' <<<"$TAGS_JSON")
done
PAYLOAD=$(jq -n \
--arg title "$TITLE" \
--arg content "$CONTENT" \
--arg status "$STATUS" \
--argjson categories "$CATEGORIES_JSON" \
--argjson tags "$TAGS_JSON" \
--arg date_gmt "${DATE%Z}" \
'{title: $title, content: $content, status: $status, categories: $categories, tags: $tags}
+ (if $date_gmt != "" then {date_gmt: $date_gmt} else {} end)')
POST_PATH="/wp-json/wp/v2/posts/"
[[ -n "$POST_ID" ]] && POST_PATH="/wp-json/wp/v2/posts/$POST_ID"
# push the post!
BODY=$(wp_api_call POST "$POST_PATH" -H "Content-Type: application/json" --data "$PAYLOAD") || exit 1
echo "$BODY"
# backup the posted data (temporarily until it is auto-removed)
mv $TMPFILE $TMPFILE.posted