-
Notifications
You must be signed in to change notification settings - Fork 0
feat(slack): add real-time message polling to chat #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Implement message polling in slack_chat() so incoming messages are displayed as they arrive. Previously, the polling block was empty. - Add slack_get_latest_ts() to get newest message timestamp - Add slack_poll_new_messages() to fetch messages since a timestamp - Track last_ts after displaying initial messages - Poll every 5 seconds and display new messages in chronological order - Update last_ts after sending to avoid showing own messages twice Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
MrFlounder
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
double check the current pull command to see if that duplicate your effort. If not then feel free to merge
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds real-time polling to crab slack chat so inbound Slack messages appear automatically during an interactive chat session, using a last-seen timestamp baseline to avoid duplicates.
Changes:
- Added helpers to fetch the latest message timestamp and to poll Slack history for messages newer than a stored timestamp.
- Implemented a periodic polling loop in
slack_chatto display new messages as they arrive. - Updated the timestamp baseline after sending messages to reduce duplicate display.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/crabcode
Outdated
| curl -s -H "Authorization: Bearer $token" \ | ||
| "https://slack.com/api/conversations.history?channel=$channel_id&limit=1" \ | ||
| | jq -r '.messages[0].ts // "0"' |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slack_get_latest_ts doesn’t validate Slack’s {ok:false, error:...} responses. On auth/rate-limit errors this will currently return "0" (or "null"), which can cause the polling baseline to reset and re-display recent history. Consider parsing .ok/.error (like slack_read_history) and returning non-zero on failure so callers can handle it explicitly.
| curl -s -H "Authorization: Bearer $token" \ | |
| "https://slack.com/api/conversations.history?channel=$channel_id&limit=1" \ | |
| | jq -r '.messages[0].ts // "0"' | |
| 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 | |
| # Do not return a bogus "0" timestamp on Slack API errors | |
| return 1 | |
| fi | |
| echo "$response" | jq -r '.messages[0].ts // "0"' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 885182d - Added .ok validation to slack_get_latest_ts. Now returns non-zero on API errors, and callers handle this gracefully.
src/crabcode
Outdated
| echo -ne "\r\033[K" | ||
|
|
||
| # Poll for new messages | ||
| local new_messages=$(slack_poll_new_messages "$channel_id" "$last_ts") |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the script runs with set -e, this command substitution will terminate the entire chat loop if slack_poll_new_messages returns non-zero (e.g. Slack returns ok:false due to ratelimited). Wrap the call in an if new_messages=$(...); then ... else ... fi (or use || true with explicit handling) so transient polling failures don’t exit the session.
| local new_messages=$(slack_poll_new_messages "$channel_id" "$last_ts") | |
| local new_messages="" | |
| if ! new_messages=$(slack_poll_new_messages "$channel_id" "$last_ts"); then | |
| # Avoid exiting the chat loop on transient Slack API errors (e.g. rate limiting) | |
| last_check=$now | |
| continue | |
| fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 885182d - Wrapped polling call in if ! new_messages=$(...) to handle errors gracefully without crashing the chat loop under set -e.
src/crabcode
Outdated
| # Update last_ts to avoid re-displaying our own message | ||
| last_ts=$(slack_get_latest_ts "$channel_id") |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updating last_ts by calling slack_get_latest_ts after sending can skip messages: if another user posts between chat.postMessage and this history fetch, the “latest” ts may advance past an unseen incoming message, and the next poll will never display it. Prefer capturing the sent message ts from the chat.postMessage response and advancing last_ts to that value (or otherwise ensure you don’t jump past unseen messages).
| # Update last_ts to avoid re-displaying our own message | |
| last_ts=$(slack_get_latest_ts "$channel_id") | |
| # Note: last_ts is updated when polling new messages to avoid skipping any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 885182d - Removed the post-send last_ts update. Now last_ts is only updated when messages are displayed in the polling loop, ensuring no messages are skipped.
src/crabcode
Outdated
| # Get messages newer than since_ts | ||
| local response=$(curl -s -H "Authorization: Bearer $token" \ | ||
| "https://slack.com/api/conversations.history?channel=$channel_id&oldest=$since_ts&limit=20") | ||
|
|
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Polling uses a fixed limit=20. If more than 20 messages arrive between polls (busy channels, long pauses), older messages in that interval will be dropped because conversations.history returns only the most recent page. Consider paginating using has_more/response_metadata.next_cursor until all messages since since_ts are drained (or increase the limit and document the tradeoff).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 885182d - Increased limit from 20 to 100. For most use cases this should be sufficient. Full pagination could be added later if needed for very high-traffic channels.
src/crabcode
Outdated
| local text=$(echo "$msg" | jq -r '.text') | ||
| 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" |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slack message text is read from the API and passed directly into echo -e without any sanitization, which allows remote users to inject terminal escape/control sequences. A malicious Slack user could send a message containing ANSI or control codes that, when rendered, can clear the screen, forge prompts, or otherwise manipulate the terminal output shown to the operator. To mitigate this, strip or escape non-printable/control characters from text before printing, or otherwise ensure only safe printable characters are written to the terminal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 885182d - Added comprehensive sanitization:
- Removes CSI sequences (colors, cursor control, screen manipulation)
- Removes OSC sequences (title/window manipulation)
- Strips control characters (bell, backspace, etc.)
Tested against multiple injection payloads including color codes, title injection, screen clear, and cursor movement attacks.
- Add Slack API error validation to slack_get_latest_ts - Handle polling errors gracefully to prevent chat loop crashes with set -e - Remove post-send last_ts update to avoid skipping messages - Increase poll limit from 20 to 100 for busy channels - Sanitize message text to prevent terminal escape sequence injection - Removes CSI sequences (colors, cursor, screen control) - Removes OSC sequences (title manipulation) - Strips control characters (bell, backspace, etc.) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
814e7d1 to
885182d
Compare
|
Checked for duplicate functionality - no overlap found. All Copilot review comments have been addressed in commit 885182d. Ready for re-review. |
Summary
crab slack chatso incoming messages are displayed as they arriveTest plan
crab slack chat @someone🤖 Generated with Claude Code