#!/bin/sh # # $Id: shtter.sh,v 1.4 2012/04/28 08:09:24 tsutsui Exp tsutsui $ # # shtter.sh - print timeline and post tweet via command line shell # http://lostman-worlds-end.blogspot.com/search/label/Twitter # See this home page for details, including original license (GPLv3) etc. # # Required commands: # From NetBSD base: # dd md5 cut date grep sort uniq hexdump rm iconv openssl # possibly sh builtins: # [ echo printf read # From pkgsrc: # gsed wget # # Modified for NetBSD by Izumi Tsutsui # - use md5(1) instead of md5sum # - use gsed (from pkgsrc) for GNU sed extentions (gsed -i etc.) # - note wget(1) (from pkgsrc) is also required # (can we eventually have --post-data in lukem ftp(1)!?) # - use utf-16le instead of utf-16 to decode timeline on big endian machines # - move "shift 1" where it's actually necessary # (XXX: sh(1) complains if shift is called without args) # - use "=" instead of "==" for test(1) (sh(1) builtin complains it) # - use gsed -e for multiple commands to improve performance # (XXX: multiple commands with ; separator seem awfully slow on x68k) # - also decode " > < & in timeline strings # - add ENCODING environment to set input/output encondings on terminal # and explicitly set LANG in script for each command # - some more cosmetics # # Notice (as original shtter.sh): # - AKEY and ASECRET are stored in this script itself if they are empty # (by gsed -i) # - modify SUFFIX and PREFIX (which are added on posts) as you like LANG=ja_JP.UTF-8 ENCODING=shift_jis #ENCODING=euc-jp #ENCODING=iso-2022-jp # for NetBSD/x68k Japanese capable console if [ "$TERM" = "x68k" ]; then ENCODING=iso-2022-jp fi CKEY="U3DhP7CpPx0bNCzdAFShg" CSECRET="eAQ13OTo7BhroXQc5dA5oOqEuXhAf7yABVKpkbR7so" AKEY="" ASECRET="" HTTP_GET="wget -q -O -" HTTP_POST="wget -q -O - --post-data" #HTTP_GET="ftp -V -o -" #HTTP_GET="curl -s" #HTTP_POST="curl -s --data" MD5SUM=md5 SED=gsed TMPDIR="/tmp" PREFIX="" #SUFFIX=" http://bit.ly/dsKezn #shtter" SUFFIX="" GenerateNonce() { dd if=/dev/urandom bs=1024 count=1 2>/dev/null | ${MD5} | cut -c1-32 } GetTimeStamp() { date +%s } Encode() { echo "$@" | ${SED} 's/./\0\n/g' | grep '[^-._~0-9a-zA-Z]' | sort | uniq | while read l do if [ "$l" = "" ]; then l=" "; fi HEX="`echo -n \"$l\" | hexdump -e '16/1 "%02X" "\n"'`" if [ "$l" = "/" ]; then l="\/"; fi echo "s/$l/$HEX" done | ${SED} -e 's/ *$//' -e 's/\([0-9A-Z]\{2\}\)/%\1/g' -e 's/$/\/g/' >"$TMPDIR/rep.sed" echo "$@" | ${SED} -f "$TMPDIR/rep.sed" rm "$TMPDIR/rep.sed" } Decode() { HEX="`echo -e "$@" | ${SED} 's/&#[0-9]\+;/\n\0\n/g' | ${SED} -e '/^&#[0-9]\+;$/!d' -e 's/[&#;]//g' | sort | uniq`" HEX="`echo -e \"$HEX\" | while read l; do echo -n \"$l\" | hexdump -e '8/1 "%02x00"'; printf '%04x\n' "$l"; done`" HEX="`echo -e \"$HEX\" | ${SED} -e 's/ 00//g' -e 's/^/73002f0026002300/' -e 's/\(..\)\(..\)$/3b002f00\2\12f0067000a00/'`" HEX="`echo $HEX | ${SED} -e 's/ //g' -e 's/../\\\x\0/g'`" printf "$HEX" | iconv -f utf-16le -t utf-8 >"$TMPDIR/rep.sed" echo -e "$@" | ${SED} -f "$TMPDIR/rep.sed" | ${SED} -e 's/\"/\"/g' -e 's/\&/\&/g' -e 's/\&/\&/g' -e 's/\>/>/g' -e 's/\</&2 exit 1 fi RKEY="`echo \"$RTOKEN\" | ${SED} 's/.*oauth_token=\([^&]*\).*/\1/'`" RSECRET="`echo \"$RTOKEN\" | ${SED} 's/.*oauth_token_secret=\([^&]*\).*/\1/'`" echo "open this url in your browsser and input pin" >&2 echo "http://twitter.com/oauth/authorize?oauth_token=$RKEY" >&2 echo -n "pin > " >&2 read PIN echo "$RKEY $RSECRET $PIN" } GetAccessToken() { RKEY="$1" RSECRET="$2" PIN="$3" URL="http://twitter.com/oauth/access_token" PARAM="oauth_consumer_key=$CKEY&oauth_nonce=`GenerateNonce`&oauth_signature_method=HMAC-SHA1&oauth_timestamp=`GetTimeStamp`&oauth_token=$RKEY&oauth_verifier=$PIN&oauth_version=1.0" HASH="`GenerateHash \"GET\" \"$URL\" \"$PARAM\"`" ATOKEN="`$HTTP_GET \"$URL?$PARAM&oauth_signature=$HASH\"`" if [ "$ATOKEN" = "" ]; then echo "can not get access token" >&2 exit 1 fi AKEY="`echo $ATOKEN | ${SED} 's/.*oauth_token=\([^&]*\).*/\1/'`" ASECRET="`echo $ATOKEN | ${SED} 's/.*oauth_token_secret=\([^&]*\).*/\1/'`" ${SED} -i "1,/^AKEY/ s/^\(AKEY=\).*/\1\"$AKEY\"/" "$0" ${SED} -i "1,/^ASECRET/ s/^\(ASECRET=\).*/\1\"$ASECRET\"/" "$0" } GetTimeLine() { URL="http://api.twitter.com/1/statuses/home_timeline.xml" PARAM="oauth_consumer_key=$CKEY&oauth_nonce=`GenerateNonce`&oauth_signature_method=HMAC-SHA1&oauth_timestamp=`GetTimeStamp`&oauth_token=$AKEY&oauth_version=1.0&status=" HASH="`GenerateHash \"GET\" \"$URL\" \"$PARAM\"`" XML="`$HTTP_GET \"$URL?$PARAM&oauth_signature=$HASH\"`" if [ "$XML" = "" ] then echo "can not get TimeLine" >&2 exit 1 fi XML="`echo $XML | ${SED} -e 's//\n\0/g' -e 's/<\/screen_name>/\0\n/g' | ${SED} -n -e '/^.*<\/screen_name>$/!d' -e 's/\([^<]*\).*\([^<]*\).*/\2: \1/' -e 'p'`" Decode "$XML" } UpdateTimeLine() { TWEET="`echo -e "$@" | iconv -f $ENCODING -t utf-8`" TWEET="`Encode \"$PREFIX$TWEET$SUFFIX\"`" if [ "$TWEET" = "" ] then echo "can not encode tweet" >&2 exit 1 fi URL="http://api.twitter.com/1/statuses/update.xml" PARAM="oauth_consumer_key=$CKEY&oauth_nonce=`GenerateNonce`&oauth_signature_method=HMAC-SHA1&oauth_timestamp=`GetTimeStamp`&oauth_token=$AKEY&oauth_version=1.0&status=$TWEET" HASH="`GenerateHash \"POST\" \"$URL\" \"$PARAM\"`" XML="`$HTTP_POST \"$PARAM&oauth_signature=$HASH\" \"$URL\"`" if [ "$XML" = "" ] then echo "can not post tweet" >&2 exit 1 fi } if [ "$AKEY" = "" -o "$ASECRET" = "" ] then RTOKEN="`GetRequestToken`" GetAccessToken $RTOKEN else ARG="$1" case "$ARG" in init) ${SED} -i "1,/^AKEY/ s/^\(AKEY=\).*/\1/" "$0" ${SED} -i "1,/^ASECRET/ s/^\(ASECRET=\).*/\1/" "$0" ;; update) shift 1 UpdateTimeLine "$@" ;; *) GetTimeLine ;; esac fi