From claude-team-toolkit
Slack post/threads/files via Web API. Use to post deploy alerts, fetch history, upload files, list channels. Multi-workspace via SLACK_PROFILE.
npx claudepluginhub tuannv14/claude-team-toolkit --plugin claude-team-toolkitThis skill is limited to using the following tools:
REST against `https://slack.com/api/`. Bearer auth with bot/user OAuth token.
Prevents silent decimal mismatch bugs in EVM ERC-20 tokens via runtime decimals lookup, chain-aware caching, bridged-token handling, and normalization. For DeFi bots, dashboards using Python/Web3, TypeScript/ethers, Solidity.
Share bugs, ideas, or general feedback.
REST against https://slack.com/api/. Bearer auth with bot/user OAuth token.
Profiles isolate workspaces.
Arguments: $ARGUMENTS. Profile resolution: --profile <name> → SLACK_PROFILE → ~/.slack/active_profile → [default].
curl, jq.
~/.slack/credentials (mode 600):
[default]
bot_token = xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx
default_channel = #general
[work]
bot_token = xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx
default_channel = #engineering
# require confirmation for any post (e.g., for sensitive workspaces)
require_confirm = true
[client_a]
bot_token = xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx
default_channel = #project-a
Get a bot token:
chat:write — post messageschannels:read — list channelsusers:read — resolve user namesfiles:write — upload filesxoxb-)/invite @YourBotNever use xoxp- user tokens unless absolutely necessary — they have
the user's full permissions and are riskier if leaked.
source "$HOME/.claude-team-toolkit/lib/credentials.sh"
source "$HOME/.claude-team-toolkit/lib/confirm.sh"
ctt_load_creds slack "$PROFILE"
slack_api() {
local method="$1" path="$2"; shift 2
curl -s -X "$method" \
-H "Authorization: Bearer $CTT_BOT_TOKEN" \
-H "Content-Type: application/json; charset=utf-8" \
"$@" \
"https://slack.com/api/$path"
}
post <channel> <message> — post a messageCHANNEL="${1:-$CTT_DEFAULT_CHANNEL}"
MESSAGE="$2"
[ "$CTT_REQUIRE_CONFIRM" = "true" ] && \
ctt_confirm "Post to $CHANNEL on $CTT_PROFILE?" || return 1
BODY=$(jq -n --arg c "$CHANNEL" --arg t "$MESSAGE" '{channel: $c, text: $t}')
RESP=$(slack_api POST chat.postMessage -d "$BODY")
echo "$RESP" | jq -r 'if .ok then "Posted: \(.ts)" else "ERROR: \(.error)" end'
ctt_audit_log slack "posted to $CHANNEL"
Channel can be #name, name, or C0XXXXXXX (channel ID).
post-blocks <channel> <blocks-json> — rich blocks messageFor deploy alerts / status posts with formatting. <blocks-json> is the
Slack Block Kit JSON array (validate at https://app.slack.com/block-kit-builder/).
BODY=$(jq -n --arg c "$CHANNEL" --argjson b "$BLOCKS" '{channel: $c, blocks: $b}')
slack_api POST chat.postMessage -d "$BODY"
thread <channel> <message-ts> <reply> — reply in threadBODY=$(jq -n \
--arg c "$CHANNEL" \
--arg ts "$MESSAGE_TS" \
--arg t "$REPLY" \
'{channel: $c, thread_ts: $ts, text: $t}')
slack_api POST chat.postMessage -d "$BODY"
channels [--limit N] — list channels bot is inslack_api GET "conversations.list?types=public_channel,private_channel&limit=${LIMIT:-100}" \
| jq -r '.channels[] | "\(.id)\t#\(.name)\t\(.num_members) members"'
history <channel> [--limit N] — recent messagesCHID="${1#\#}"
# Validate Slack channel name/ID: a-z, 0-9, hyphen, underscore only
case "$CHID" in
''|*[!a-zA-Z0-9_-]*) echo "Invalid channel: $CHID" >&2; return 1 ;;
esac
case "$LIMIT" in
''|*[!0-9]*) LIMIT=20 ;;
esac
ENCODED=$(printf %s "$CHID" | jq -sRr @uri)
slack_api GET "conversations.history?channel=$ENCODED&limit=$LIMIT" \
| jq -r '.messages[] | "\(.ts)\t\(.user // .bot_id)\t\(.text | sub("\n"; " "; "g")[:120])"'
upload <channel> <file-path> [--comment <text>] — upload fileSlack now uses 2-step upload (files.getUploadURLExternal + files.completeUploadExternal):
SIZE=$(stat -c %s "$FILE" 2>/dev/null || stat -f %z "$FILE")
NAME=$(basename "$FILE")
# Step 1: get upload URL
RESP1=$(slack_api GET "files.getUploadURLExternal?filename=$(printf %s "$NAME" | jq -sRr @uri)&length=$SIZE")
UPLOAD_URL=$(echo "$RESP1" | jq -r '.upload_url')
FILE_ID=$(echo "$RESP1" | jq -r '.file_id')
# Step 2: PUT file content
curl -s -X POST "$UPLOAD_URL" --data-binary "@$FILE" >/dev/null
# Step 3: complete and share
BODY=$(jq -n --arg fid "$FILE_ID" --arg ch "$CHANNEL" --arg c "${COMMENT:-}" '{
files: [{id: $fid, title: "Uploaded by claude-team-toolkit"}],
channel_id: $ch,
initial_comment: $c
}')
slack_api POST files.completeUploadExternal -d "$BODY"
users — list workspace usersslack_api GET users.list | jq -r '.members[] | select(.deleted == false) | "\(.id)\t\(.name)\t\(.profile.real_name)"'
#general or external/shared channels.require_confirm profile flag can enforce confirmation for ANY post.missing_scope, re-install app with
the needed scope, don't escalate to user token.history may contain sensitive info — treat as
confidential, don't paste publicly.chat.postMessage is Tier 4. See Retry-After header on 429.Bot tokens are per-app-per-workspace. Create one Slack app per use case (deploy notifier, on-call alerter, etc.) so revoking one doesn't kill others.