Skip to content

mng/slack-question (subsequent)#912

Merged
joshalbrecht merged 10 commits intomainfrom
mng/slack-question
Mar 20, 2026
Merged

mng/slack-question (subsequent)#912
joshalbrecht merged 10 commits intomainfrom
mng/slack-question

Conversation

@joshalbrecht
Copy link
Contributor

Automated PR created by Claude Code session.

joshalbrecht and others added 10 commits March 18, 2026 14:45
…ges and replies

The reactions.list Slack API requires a bot token, which fails with
user tokens (not_allowed_token_type). Instead, extract reactions from
the inline reactions field already present in message and reply
payloads.

New features:
- ReactionEvent: tracks per-message reaction state extracted from
  inline data, keyed by (channel_id, message_ts)
- RelevantThreadEvent: tracks threads where the authenticated user
  replied or was mentioned, stored in relevant_threads/ stream
- Reaction scan: re-fetches last 100 messages per channel and
  re-checks recent relevant threads for reaction changes, since
  reactions can be added without changing latest_reply
- --reaction-lookback N flag (default 10): controls how many recent
  relevant threads to re-check for reactions

Removed:
- reactions.list API call (fetch_user_reactions)
- ReactionItemEvent data type
- derive_reaction_item_key function

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add test_run_export_reaction_lookback_rechecks_relevant_threads which
exercises the reaction_lookback > 0 code path: verifies that relevant
threads with unchanged latest_reply are re-fetched and reactions are
extracted. Also remove stale references to reactions.list from field
descriptions, README comments, and leftover test fixture entries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…fied

Previously --channels defaulted to just "general". Now when --channels
is omitted, the exporter processes all channels from the fetched
channel list (already filtered to member channels by default). Passing
--channels explicitly still restricts to the named channels.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All slack exporter events now use source="slack" instead of
per-data-type sources (messages, replies, channels, etc.), and event
types use the _created suffix (e.g. message_created, reply_created)
instead of _fetched/_extracted/_detected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Event types are now just the entity name (e.g. "message", "reply",
"channel") rather than "message_created", "reply_created", etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add test_run_export_all_channels_when_channels_is_none to verify the
default behavior of exporting all member channels. Also fix README
backfill description to say "oldest date already searched from"
(not "oldest message"), and add missing reaction_lookback to the
refresh test settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wrap the API caller with with_rate_limit_retry() which catches
"ratelimited" SlackApiError responses and retries with exponential
backoff (starting at 2s, doubling up to 60s, max 7 retries for ~3
minutes total). The retry wraps all Slack API calls since it is
applied at the caller level in main.py.

The sleep function is injected as a parameter to enable testing
without real delays or unittest.mock.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…list

The conversations.list API does not reliably include the last_read
field across all channel types, resulting in most unread markers being
missed. Now fetches last_read per channel via conversations.info,
which reliably returns it for channels the user has joined.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ons.info

Use the latest message timestamp from conversations.info (already
fetched for unread markers) to determine which channels have new
messages. Channels where latest hasn't changed since the last export
skip the conversations.history forward fetch entirely.

The conversations.info calls are no longer cached with the channel
list since we need fresh latest timestamps on every run. Channel
metadata (names, IDs, membership) is still cached with TTL.

The reaction scan still runs for all channels (via the last 100
messages fetch) and doubles as the message source for reply/thread
detection when the forward fetch was skipped.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… function

Replace with_rate_limit_retry (which returned a closure) with
retry_on_rate_limit (a module-level function that takes the api_caller,
sleep_fn, method, and params directly). This eliminates the inline
function ratchet bump (back to 1).

The time.sleep ratchet bump (0->1) remains because the regex catches
all forms of importing sleep from the time module. This is a
legitimate use of time.sleep for rate limit backoff -- the ratchet
description says to "poll for the condition" instead, but rate limit
backoff is not a polling situation; we genuinely need to wait before
retrying.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@joshalbrecht joshalbrecht merged commit 1f12844 into main Mar 20, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant