#!/usr/bin/env bash
# Fetch list of various types of objects from Slack API
# Copyright (C) 2015 Kolbe Kegel <kolbe@kolbekegel.com>
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# this script gets a list of channels or users. if you source the script from an interactive shell,
# it'll create exported environment variables. this facilitates a sort of caching that makes 
# repeated invocation of scripts like slack_channel_history much faster. you can specify multiple
# types of "objects", but that's really only useful if you are sourcing the script. if you execute
# the script with multiple arguments, you'll have to look for a blank line in the output to switch
# from parsing one of the objects to the next.
# 
# you must define and export SLACK_API_TOKEN in your shell's environment. get a token here: https://api.slack.com/web
# you must have jq installed: http://stedolan.github.io/jq/

abort() {
    printf "[ERROR]: %s. Aborting.\n" "$1" >&2
    [[ $- = *i* ]] || exit 1
    return 1 
}

type jq &>/dev/null || abort "you don't appear to have the jq tool: http://stedolan.github.io/jq/"

[[ $1 ]] || abort "you must specify an object type ('channels' or 'users')"

for arg; do 

    case $arg in
        c*) object=channels; filter='.channels[] | .name+":"+.id' ;;
        u*) object=users; filter='.members[] | .id+":"+.name+":"+.profile.email+":"+.profile.display_name+":"+.profile.real_name' ;;
        g*) object=groups; filter='.groups[] | .name+":"+.id' ;;
        *) abort "unknown object type '$1'";;
    esac

    slack_api_cmd(){
        local cursor= temp=
        while true; do
            temp=$( curl --silent "https://slack.com/api/$object.list?token=${SLACK_API_TOKEN:?}&cursor=$cursor" ) || return
            cursor=$( jq .response_metadata.next_cursor <<<"$temp" )
            jq -r -e 'if .ok==true then '"$filter"' else error(.error) end' 2>&1 <<<"$temp" || return
            [[ $cursor = null ]] && break
        done
        # maybe a future version of jq will provide some other mechanism for this, but
        # i needed to force jq to exit with a non-zero exit code if the fetch from the API failed
    }

    if ! slack_api_output=$(slack_api_cmd); then
        abort "couldn't fetch $object list ($slack_api_output)"
    fi

    if [[ $- = *i* ]]; then
        # if the shell is interactive, that means this file is being "sourced",
        # so we set a variable in the user's shell and export it. that basically
        # makes a persistent cache of the object we fetched, which makes
        # invocations of things like slack_channel_history *much* faster, since
        # they do not have to fetch user and channel lists every time they run

        declare -x "slack_${object}_list=$slack_api_output"

        # this is an associative array version, which only works in bash 4+ 
        if (( BASH_VERSINFO[0] >= 4 )); then
            varname=slack_${object}
            unset "$varname"
            declare -A "$varname"
            while IFS=: read -r k v; do
                printf -v "$varname[$k]" %s "$v"
            done <<<"$slack_api_output"
        fi

        # here's another solution:
        #printf -v "slack_${object}_list" %s "$slack_api_output"
        #export "slack_${object}_list"

        # and if none of those work, you should read http://mywiki.wooledge.org/BashFAQ/006
    else
        # if the shell is *not* interactive, this file is being executed, so we'll just dump
        # the results to stdout and the caller can parse them as they see fit.
        printf "%s\n" "$slack_api_output"
    fi

    unset slack_api_output
    (($#>1)) && printf '\n'
    true # or failure of that argc check will cause script to exit with failure!
done
