Skip to content

Instantly share code, notes, and snippets.

@hacolab
Last active August 2, 2019 11:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hacolab/d7d56fba88cde1d5519416d9128f4cf0 to your computer and use it in GitHub Desktop.
Save hacolab/d7d56fba88cde1d5519416d9128f4cf0 to your computer and use it in GitHub Desktop.
hatenablog's AtomPub request command
#!/bin/sh
################################################################################
# This script is only send HTTP request to HatenaBlog's AtomPub API service,
# and output received HTTP response body to stdout.
# If GET request to entry collection page, auto try GET to next page.
#
#-[Usage]
#- $0 GET {Path} [-a Auth-File][-v][-n FD][-l Num[:Target]]
#- $0 DELETE {Path} [-a Auth-File][-v]
#- $0 PUT {Path} [-a Auth-File][-v][-f Tx-File]
#- $0 POST {Path} [-a Auth-File][-v][-f Tx-File]
#-
# [Parameters]
# {Path} Send request URL.
# If start with 'https://', {Path}
# Else, https://blog.hatena.ne.jp/{HatenaID}/{BlogID}/atom[/{Path}]
# [Options]
# -a Auth-File Read service auth-fino from Auth-File. see [Auth-File].
#
# -v Print HTTP header to stderr.(use curl -v option)
#
# -n FD Next page URL output to FD.
# FD is file descriptor(1 or 2), or file path. default 2.
# If file exist, overwrite file.
#
# -l Num[:Target] Limit count of match 'Target' pattern count.
# If match count >= Num, don't get next link page, command exit.
# If 'Target' is not set, count to "<entry>". pattern is grep -E use.
# If not set '-l', get next page while found for '<link rel="next"'
#
# -f Tx-File POST or PUT HTTP request body contents from Tx-File.
# If not set `-f`, read from stdin.
# contents format is XML. See [Reference].
# [STDIN]
# -
# [STDOUT]
# HatenaBlog AtomPub response body
# [STDERR]
# see '-n', '-v' in [Options]
# [Auth-File]
# Default path $HOME/.local/share/hateblog/auth
# Format BLOG_ID={YourHatenaID} exp.) example.hatenablog.com
# HATENA_ID={YourHatenaID}
# API_KEY={YourKey}
# [Dependency]
# curl https://curl.haxx.se/
#
# [Reference]
# http://developer.hatena.ne.jp/ja/documents/blog/apis/atom
#
################################################################################
CMD_NAME=`basename $0`
VERSION=0.1.0
# Help & Error
usage_exit() {
echo "$1" 1>&2
sed -n '/^#-/s/^#-//p' "$0" | sed "s/\$0/$CMD_NAME/g" 1>&2
exit 200
}
version_exit() {
echo "VERSION: $VERSION" && exit 0
}
error_exit() {
echo "ERROR: $1" 1>&2 && exit 200
}
# Local config
LOCAL_DIR="$HOME/.local/share/hateblog"
AUTH_FILE="$LOCAL_DIR/auth"
TMP_HEADER="$LOCAL_DIR/$CMD_NAME.header.$$"
TMP_BODY="$LOCAL_DIR/$CMD_NAME.body.$$"
[ ! -d "$LOCAL_DIR" ] && mkdir -p "$LOCAL_DIR"
if [ ! -f "$AUTH_FILE" ]; then
cat <<- AUTHEND > "$AUTH_FILE"
BLOG_ID=
HATENA_ID=
API_KEY=
AUTHEND
chmod 600 "$AUTH_FILE"
fi
# Fetch Parameters
HTTP_METHOD="$1"
REQUEST_URL="$2"
if [ `echo "$REQUEST_URL" | grep -c '^-'` -ne 0 ]; then
usage_exit 'few parameter!'
fi
shift 2
# Analyze options
TX_FILE=-
LIMIT_COUNT=0
PRINT_HEADER=
PRINT_NEXT_URL=
FD_OF_NEXT_URL=2
while getopts a:f:l:n:vV OPT; do
case "$OPT" in
a ) AUTH_FILE="$OPTARG"
;;
f ) TX_FILE="$OPTARG"
[ ! -f "$TX_FILE" ] && error_exit "Tx-File dose not found: '$TX_FILE'"
;;
l ) # -l Limit-Count[:pattern]
if [ `echo "$OPTARG" | grep -c ":"` -eq "0" ]; then
LIMIT_COUNT=$OPTARG
COUNT_PATTERN="<entry>"
else
LIMIT_COUNT="${OPTARG%%:*}"
COUNT_PATTERN="${OPTARG##*:}"
fi
;;
n ) PRINT_NEXT_URL=true
FD_OF_NEXT_URL="$OPTARG"
;;
v ) PRINT_HEADER=true
;;
V ) version_exit
;;
\?) usage_exit
;;
esac
done
shift `expr $OPTIND - 1`
# Load default auth info
[ ! -f "$AUTH_FILE" ] && error_exit "Auth-File does not found: '$AUTH_FILE'"
get_value() {
sed -n "s/^$1=\(.*\)[^ ]*$/\1/p"
}
BLOG_ID=`get_value BLOG_ID < "$AUTH_FILE"`
HATENA_ID=`get_value HATENA_ID < "$AUTH_FILE"`
API_KEY=`get_value API_KEY < "$AUTH_FILE"`
# Check user auth info
[ -z "$BLOG_ID" ] && error_exit "'BLOG_ID' is null!"
[ -z "$API_KEY" ] && error_exit "'API_KEY' is null!"
[ -z "$HATENA_ID" ] && error_exit "'HATENA_ID' is null!"
# If not full path given.
HATENA_ROOT='https://blog.hatena.ne.jp'
if [ `echo "$REQUEST_URL" | grep -c "^https://"` -eq 0 ]; then
REQUEST_URL="$HATENA_ROOT/$HATENA_ID/$BLOG_ID/atom${REQUEST_URL:+"/$REQUEST_URL"}"
fi
# Send request
CURL_OPT="-s -H 'Content-Type: applicatoin/xml' -X $HTTP_METHOD -u $HATENA_ID:$API_KEY"
[ -n "$PRINT_HEADER" ] && CURL_OPT="$CURL_OPT -v"
get_request_pages() {
# Clear temp
>| "$TMP_HEADER"
# Get collection pages
next_page=$1
limit_count=$3
while :
do
# Send HTTP request
curl $CURL_OPT "$next_page" | tee "$TMP_BODY"
# Next page exist?
next_page=`grep '<link rel="next"' < "$TMP_BODY" | sed 's/^.*href="\([^"]*\)".*/\1/'`
[ -z "$next_page" ] && break
# No limit?
[ "$limit_count" -eq 0 ] && continue
# Over limit?
match_count=`grep -Ec "$2" "$TMP_BODY"`
[ "$limit_count" -le "$match_count" ] && break;
limit_count=`expr $limit_count - $match_count`
done >&1
# Print next page URL
[ -z "$PRINT_NEXT_URL" ] && return
fomated_next_url="NEXT>$next_page"
case "$FD_OF_NEXT_URL" in
1 ) echo "$fomated_next_url" >&1 ;;
2 ) echo "$fomated_next_url" 1>&2 ;;
* ) echo "$fomated_next_url" > "$FD_OF_NEXT_URL" ;;
esac
}
case "$HTTP_METHOD" in
GET ) get_request_pages "$REQUEST_URL" "$COUNT_PATTERN" "$LIMIT_COUNT" ;;
DELETE ) curl $CURL_OPT "$REQUEST_URL" ;;
PUT|POST ) curl $CURL_OPT --data-binary @"$TX_FILE" "$REQUEST_URL" ;;
* ) error_exit "Invalid method '$HTTP_METHOD'" ;;
esac
# Clean up
rm "$TMP_HEADER" "$TMP_BODY" 2>/dev/null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment