~kmaasrud/shatgpt

710ba21435e2177a43f5a8fe67b8c74701c21449 — Knut Magnus Aasrud 11 months ago 230eead
cool warmup, now here is a POSIX ChatGPT client
2 files changed, 97 insertions(+), 96 deletions(-)

A shatgpt
D wsh
A shatgpt => shatgpt +97 -0
@@ 0,0 1,97 @@
#!/usr/bin/env dash

has() {
    # check if a command exists
    # $1 - cmd to check
    cmd=$(command -v "$1") 2>/dev/null || return 1
    [ -x "$cmd" ] || return 1
}

clear_line() {
    # clear the current line and place cursor at the beginning
    printf '\r\033[K'
}

spinner() {
    # display a spinner animation
    # $1 - message to display
    spinner_chars="⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏"
    while :
    do
        for char in $spinner_chars
        do
            clear_line
            printf '%s %s' "$char" "$1"
            sleep 0.05
        done
    done
}

make_body() {
    # make a body to send to the openai chat api
    # $1 - prompt
    printf '%s\n' "{\"model\": \"gpt-3.5-turbo\", \"stream\": true, \"messages\": [{\"role\": \"user\", \"content\": \"$1\"}]}"
}

get_field() {
    # get a field from a json string by grepping
    # $1 - json string
    # $2 - field to grep
    printf '%s\n' "$1" | grep -o "\"$2\":\"[^\"]*" | sed "s/\"$2\":\"//"
}

process_event() {
    # process each streaming event
    delta=$(get_field "$1" content)
    if [ -n "$delta" ]; then
	printf '%b' "$delta"
    fi
}

stream_events() {
    # do http request from a url
    # $1 - url
    # $2 - data
    spinner "Establishing connection..." &
    spinner_pid=$!

    if has curl; then
	curl "$1" -d "$2" -s -N -H "Content-Type: application/json" -H "Authorization: Bearer $OPENAI_API_KEY" | while IFS= read -r event; do
	    if kill $spinner_pid 2>/dev/null; then
		clear_line
	    fi
            process_event "$event"
        done
    else
        printf '%s\n' "Error: curl not found"
        exit 1
    fi
}

cleanup() {
    # cleanup function to be called on script termination
    # Add any cleanup tasks here
    kill $spinner_pid 2>/dev/null
    printf '\n'
    exit 0
}

trap cleanup INT TERM

main() {

    stream_events "https://api.openai.com/v1/chat/completions" "$(make_body "$*")"

    cleanup

    # content=$(get_field "$response" content)
    #
    # kill "$spinner_pid" >/dev/null 2>&1
    # clear_line
    #
    # printf '%s\n' "$response"
    # printf '\n%s\n' "$response" | json '"choices"' 0 '"message"' '"content"'
    # printf '\n%s\n' "$response" | json '"choices"' 0 '"message"' '"content"' | trim_quotes | fold -sw 80
}

main "$*"

D wsh => wsh +0 -96
@@ 1,96 0,0 @@
#!/usr/bin/env sh

has() {
    # check if a command exists
    # $1 - cmd to check
    cmd=$(command -v "$1") 2>/dev/null || return 1
    [ -x "$cmd" ] || return 1
}

trim() {
    # trim whitespace from the beginning and end of a string
    # reads from stdin
    read trim
    trim=${trim#${trim%%[![:space:]]*}}
    trim=${trim%${trim##*[![:space:]]}}
    trim=${trim%$'\n'}
    printf '%s\n' "$trim"
}

bolden() {
    # make a string bold
    # reads from stdin
    read bold
    printf '\033[1m%s\033[0m\n' "$bold"
}

urlencode() {
    # url encode a string
    # $1 - string to encode
    while IFS= read -r c;
    do
        case "$c" in
            [a-zA-Z0-9.~_-])
                printf "$c"
                continue
            ;;
        esac
        printf '%s' "$c" | od -An -tx1 | xargs printf "%%%s"
    done <<EOF
$(printf '%s' "$1" | fold -w1)
EOF
}

make_url() {
    # make a url to fetch summary data from wikipedia
    # $1 - query
    url="https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&explaintext&redirects=1&titles=$(urlencode "$1")"
    printf '%s\n' "$url"
}

fetch_data() {
    # fetch summary data from a wikipedia api url
    # $1 - url
    if has wget; then
        printf '%s\n' "$(wget -qO- "$1")"
    elif has curl > /dev/null; then
        printf '%s\n' "$(curl -s "$1")"
    else
        printf '%s\n' "Error: wget or curl not found"
        exit 1
    fi

}

grep_field() {
    # grep a field from a json string
    # $1 - json string
    # $2 - field to grep
    printf '%s\n' "$1" | grep -o "\"$2\":\"[^\"]*" | sed "s/\"$2\":\"//"
}

main() {
    case "$1" in
        -h|--help|"")
            printf '%s\n' "Usage: $0 <query>"
            exit 0
        ;;
    esac

    url=$(make_url "$*")
    data=$(fetch_data $url)
    title=$(grep_field "$data" title)
    summary=$(grep_field "$data" extract)

    if [ -z "$summary" ]; then
        printf '%s\n' "No results found for \"$@\""
        exit 1
    fi

    printf '\n'
    printf '%s\n' "$title" | bolden
    printf '%b\n' "$summary" | trim | fold -sw 80
    printf '\n'
}

main "$*"