Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 79 additions & 1 deletion src/crabcode
Original file line number Diff line number Diff line change
Expand Up @@ -2573,6 +2573,40 @@ slack_display_messages() {
done <<< "$messages"
}

# Get timestamp of latest message in a channel (for polling baseline)
slack_get_latest_ts() {
local channel_id="$1"
local token=$(slack_get_token)

local response=$(curl -s -H "Authorization: Bearer $token" \
"https://slack.com/api/conversations.history?channel=$channel_id&limit=1")
local ok=$(echo "$response" | jq -r '.ok')
if [ "$ok" != "true" ]; then
return 1
fi
echo "$response" | jq -r '.messages[0].ts // "0"'
}

# Poll for new messages since a given timestamp
slack_poll_new_messages() {
local channel_id="$1"
local since_ts="$2"
local token=$(slack_get_token)

# Get messages newer than since_ts (limit=100 to handle busy channels)
local response=$(curl -s -H "Authorization: Bearer $token" \
"https://slack.com/api/conversations.history?channel=$channel_id&oldest=$since_ts&limit=100")

local ok=$(echo "$response" | jq -r '.ok')
if [ "$ok" != "true" ]; then
return 1
fi

# Return messages (excluding the one at exactly since_ts), in chronological order
echo "$response" | jq -c --arg ts "$since_ts" \
'.messages | map(select(.ts != $ts)) | reverse | .[]'
}

# Interactive chat mode
slack_chat() {
local target="$1"
Expand Down Expand Up @@ -2624,6 +2658,12 @@ slack_chat() {
echo ""
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"

# Get latest message timestamp for polling baseline
local last_ts
if ! last_ts=$(slack_get_latest_ts "$channel_id"); then
last_ts="0"
fi

# Chat loop
local last_check=$(date +%s)

Expand All @@ -2635,14 +2675,52 @@ slack_chat() {
if [ -n "$message" ]; then
if slack_post_message "$channel_id" "$message"; then
echo -e "${GRAY}[sent]${NC}"
# Note: last_ts is updated when polling new messages to avoid skipping any
# messages that arrive between send and the next poll
fi
fi
fi

# Poll for new messages every 5 seconds
local now=$(date +%s)
if [ $((now - last_check)) -ge 5 ]; then
# Check for new messages (would need to track last seen timestamp for proper implementation)
# Clear the prompt line before showing new messages
echo -ne "\r\033[K"

# Poll for new messages (handle errors gracefully to avoid crashing chat loop)
local new_messages=""
if ! new_messages=$(slack_poll_new_messages "$channel_id" "$last_ts"); then
# Transient API error (e.g., rate limiting) - skip this poll cycle
last_check=$now
continue
fi

if [ -n "$new_messages" ]; then
while IFS= read -r msg; do
local ts=$(echo "$msg" | jq -r '.ts')
local time=$(echo "$msg" | jq -r '.ts | tonumber | strftime("%H:%M")')
local user_id=$(echo "$msg" | jq -r '.user // empty')
# Sanitize text to prevent terminal escape sequence injection
# Remove CSI sequences (\033[...X), OSC sequences (\033]...\007), then control chars
local text=$(echo "$msg" | jq -r '.text' | sed $'s/\033\\[[0-9;]*[a-zA-Z]//g; s/\033\\][^\007]*\007//g' | tr -d '\000-\010\013\014\016-\037')
local bot_name=$(echo "$msg" | jq -r '.bot_profile.name // empty')

local sender
if [ -n "$bot_name" ]; then
sender="$bot_name"
elif [ -n "$user_id" ]; then
sender=$(slack_get_username_cached "$user_id")
else
sender="unknown"
fi

echo -e "${GRAY}[$time]${NC} ${CYAN}$sender${NC}: $text"

# Update last seen timestamp
last_ts="$ts"
done <<< "$new_messages"
fi

last_check=$now
fi
done
Expand Down