Skip to content

Team_id-based Slack bot tokens and test updates (#91)#95

Merged
snowfox1003 merged 8 commits intocppalliance:developfrom
leostar0412:dev-91
Mar 9, 2026
Merged

Team_id-based Slack bot tokens and test updates (#91)#95
snowfox1003 merged 8 commits intocppalliance:developfrom
leostar0412:dev-91

Conversation

@leostar0412
Copy link
Copy Markdown
Collaborator

@leostar0412 leostar0412 commented Mar 5, 2026

Summary

Adds team_id-based Slack bot token resolution so the app can use different bot tokens per Slack workspace. Config, token helpers, and the CPPA Slack tracker are updated to use team_id, and tests are aligned with the new API and missing-team_id behavior.

Fixes #91


What changed

Config & environment

  • config/settings.pySLACK_BOT_TOKEN is now a dict (team_id → token) built from:
    • SLACK_TEAM_IDS (comma-separated)
    • SLACK_BOT_TOKEN_<team_id> per team
  • .env.example — Documents SLACK_TEAM_IDS and SLACK_BOT_TOKEN_<team_id> for multi-workspace setup

Token API (operations/slack_ops/tokens.py)

  • get_slack_bot_token(team_id)team_id is required; raises ValueError when missing or when the team has no token
  • get_slack_client(team_id=...) — When no explicit token is passed, uses get_slack_bot_token(team_id) so callers must supply team_id

CPPA Slack tracker

  • Fetcher, command, services — Use team_id (from channel/workspace or options) and call get_slack_client(team_id=...)
  • Sync (sync_channel, sync_channel_user, sync_message, sync_user) — Signatures and call sites updated to pass team_id through the pipeline

Tests

  • operations/tests/test_slack_tokens.py — Tests updated for the new API; new test ensures missing/empty team_id raises with a clear message
  • cppa_slack_tracker/tests/test_sync_message.py — Additional coverage (e.g. merge-by-ts behavior)

Summary by CodeRabbit

  • New Features

    • Multi-workspace Slack support with per-team bot tokens and explicit team-id resolution; CLI now defaults to syncing users, channels, and messages.
    • API-driven channel/member selection with single-channel or full-team sync; team-aware user resolution throughout.
  • Bug Fixes

    • Merge messages by timestamp to avoid duplicates and preserve updates.
    • Improved date-range handling and stricter validation/error reporting for missing team IDs/tokens.
    • Removed legacy file-based import flows in favor of API-first syncing.
  • Logging

    • Progress and dry-run output converted to structured logging.
  • Tests

    • Added tests for message-merge logic and token fallback behavior.
  • Documentation

    • Updated configuration examples and guidance for multi-workspace setup.

@leostar0412 leostar0412 self-assigned this Mar 5, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

Replace single-workspace Slack token usage with a multi-workspace mapping (SLACK_TEAM_IDS + per-team SLACK_BOT_TOKEN_), thread team_id through token/client acquisition, and move channel/user/message sync flows from file-based recovery to API-driven retrieval with merged raw messages by timestamp.

Changes

Cohort / File(s) Summary
Config & env examples
\.env.example, config/settings.py
Introduce SLACK_TEAM_IDS and per-team SLACK_BOT_TOKEN_<id> examples; add _slack_bot_token_from_env() to build SLACK_BOT_TOKEN as a dict mapping team_id → token (replaces single-string token).
Token resolution & client factory
operations/slack_ops/tokens.py, operations/tests/test_slack_tokens.py
Add _slack_team_id_fallback() and get_slack_bot_token(team_id) with validation; change get_slack_client(..., team_id) to resolve tokens by team_id and raise on missing config; tests updated for fallback/error cases.
Fetcher & client usage
cppa_slack_tracker/fetcher.py
Thread team_id into Slack client acquisition (get_slack_client(team_id=...)); add team_id params to fetch functions; allow start_date=None for unbounded message fetches and update message filtering accordingly.
Services & user resolution
cppa_slack_tracker/services.py, cppa_slack_tracker/tests/test_services.py
Propagate team_id into user-resolution helpers; remove creator_user_id parameter (derive creator from payload); update tests to match signature changes.
Channel discovery & membership sync
cppa_slack_tracker/sync/sync_channel.py, cppa_slack_tracker/sync/sync_channel_user.py, cppa_slack_tracker/sync/__init__.py
Remove channels.json file I/O; always fetch channels from API (conversations.info, fetch_channel_list); add get_channels_to_sync(team, *, channel_id=None) and re-export it (replaces previous export).
Message sync & raw merging
cppa_slack_tracker/sync/sync_message.py, cppa_slack_tracker/tests/test_sync_message.py
Allow unbounded start_date to fetch all messages up to end_date; add _merge_messages_by_ts() to merge raw payloads by ts; update per-day/raw merging behavior and tests.
User sync & API-first flows
cppa_slack_tracker/sync/sync_user.py
Remove local users.json processing; require team_id and fetch users via fetch_user_list(team_id) with explicit error handling for missing team_id.
CLI, transcript, listener & runners
cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py, cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py, cppa_slack_transcript_tracker/runner.py, cppa_slack_transcript_tracker/utils/slack_listener.py
Switch token acquisition to get_slack_bot_token(team_id=...) using settings.SLACK_TEAM_ID; replace many stdout prints with logger calls; improve docstrings and error logging.
Public API surface & tests
cppa_slack_tracker/sync/__init__.py, various tests
Swap exported sync_channel_members for get_channels_to_sync; update unit tests to reflect token/team_id flows and changed signatures.

Sequence Diagram(s)

sequenceDiagram
    participant Config as Config/Settings
    participant TokenOps as TokenOps (tokens.py)
    participant Client as ClientFactory
    participant Fetcher as Fetcher/Sync
    participant SlackAPI as Slack API
    participant DB as Database

    Note over Config,Fetcher: Multi-workspace token resolution + API-driven sync

    Fetcher->>TokenOps: request token (team_id)
    TokenOps->>Config: lookup SLACK_BOT_TOKEN[team_id] / SLACK_TEAM_ID fallback
    alt token found
        TokenOps-->>Fetcher: return token
    else token missing
        TokenOps-->>Fetcher: raise ValueError / log error
    end

    Fetcher->>Client: get_slack_client(team_id=..., bot_token=token)
    Client->>Client: construct Slack API client with token
    Client-->>Fetcher: client instance

    Fetcher->>SlackAPI: call conversations.* / users.* / channels.* (team-scoped)
    SlackAPI-->>Fetcher: responses (channels, users, messages)
    Fetcher->>Fetcher: merge/process messages by ts
    Fetcher->>DB: persist users/channels/messages
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • iTinkerBell
  • jonathanMLDev

Poem

🐇 Hopping through envs with a neat little map,

Team IDs aligned, no token mishap.
Clients fetch in context, channels found by API,
Messages stitched by ts as raw files say goodbye.
A rabbit cheers: syncs now run in a snap!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Team_id-based Slack bot tokens and test updates' clearly summarizes the main change: introducing team_id-based bot token resolution for multi-workspace Slack support.
Linked Issues check ✅ Passed All seven coding objectives from issue #91 are addressed: sync_user.py pattern alignment, _get_client team-scoped tokens, duplicate channel logic removal via get_channels_to_sync, sync_message.py step comments updated, sync_channel_members/sync_channel_users distinction clarified, members.json read removed and API-based flow established, and sync_channel.py API fetch ensured.
Out of Scope Changes check ✅ Passed All changes align with issue #91 objectives: token handling refactoring, team_id propagation through fetcher/sync/services, configuration updates for multi-workspace support, and test updates. No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 82.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
cppa_slack_tracker/sync/sync_channel_user.py (1)

69-71: Remove redundant exception interpolation in logger.exception.

logger.exception() automatically logs the active exception and traceback. Including e in the format string duplicates this information.

♻️ Proposed cleanup
-            logger.exception(
-                "Failed to fetch/sync members for %s: %s", channel.channel_id, e
-            )
+            logger.exception(
+                "Failed to fetch/sync members for %s", channel.channel_id
+            )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_channel_user.py` around lines 69 - 71, In
sync_channel_user.py, inside the exception handling where logger.exception is
called (the block that logs "Failed to fetch/sync members for %s: %s"), remove
the redundant exception interpolation by passing only the channel identifier
(e.g., channel.channel_id) as the formatting argument and let logger.exception
capture the exception/traceback automatically; update the logger.exception call
used in the member sync/fetch function so it reads a single-format-argument
message referencing channel.channel_id (do not include the exception object `e`
in the format args).
cppa_slack_tracker/sync/sync_message.py (1)

187-189: Minor: Redundant conditional assignment.

The expression start_date = last_d if last_d is not None else None is equivalent to start_date = last_d since last_d is already None when the condition fails.

✨ Suggested simplification
-        start_date = last_d if last_d is not None else None
+        start_date = last_d
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_message.py` around lines 187 - 189, The
conditional assignment setting start_date is redundant; simply assign the result
of _last_message_date(channel) directly to start_date (e.g., start_date =
_last_message_date(channel)) or set start_date = last_d after calling
_last_message_date(channel), removing the if-else expression that checks for
None; update the assignment near the _last_message_date(channel) call and the
start_date variable.
cppa_slack_tracker/fetcher.py (1)

224-244: Consider consolidating the duplicated filter logic.

The filter logic for bounded vs. unbounded start_date has similar structure that could potentially be refactored. However, given the different conditions (start_date <= created_d <= end_date vs. created_d <= end_date), keeping them separate for clarity is also reasonable.

✨ Optional: Consolidated filter logic
-    # Filter by date: [start_date, end_date] or <= end_date when start_date is None
     messages = []
     for msg in all_messages:
         created_d = _ts_to_utc_date(msg.get("ts"))
-        if start_date is not None:
-            if created_d and start_date <= created_d <= end_date:
-                messages.append(msg)
-                continue
-            edited = msg.get("edited") or {}
-            edited_d = _ts_to_utc_date(edited.get("ts"))
-            if edited_d and start_date <= edited_d <= end_date:
-                messages.append(msg)
-        else:
-            if created_d and created_d <= end_date:
-                messages.append(msg)
-                continue
-            edited = msg.get("edited") or {}
-            edited_d = _ts_to_utc_date(edited.get("ts"))
-            if edited_d and edited_d <= end_date:
-                messages.append(msg)
+        edited = msg.get("edited") or {}
+        edited_d = _ts_to_utc_date(edited.get("ts"))
+        
+        def in_range(d: date | None) -> bool:
+            if d is None:
+                return False
+            if start_date is not None:
+                return start_date <= d <= end_date
+            return d <= end_date
+        
+        if in_range(created_d) or in_range(edited_d):
+            messages.append(msg)
     return messages
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/fetcher.py` around lines 224 - 244, The message
date-filtering block duplicates nearly identical logic for created vs edited
timestamps; refactor by extracting a small helper (e.g., _in_range(date,
start_date, end_date) or _check_and_append(msg, timestamp_key)) and use it to
evaluate both msg.get("ts") and msg.get("edited")["ts"] for each msg, replacing
the two branches that check start_date is None vs bounded; keep using
_ts_to_utc_date, all_messages, start_date, end_date and ensure behavior remains:
when start_date is None treat check as date <= end_date, otherwise enforce
start_date <= date <= end_date, appending msg if either created or edited
timestamp matches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@config/settings.py`:
- Around line 175-192: get_slack_client currently fails for no-arg calls because
SLACK_BOT_TOKEN is a dict; update get_slack_client to accept optional team_id
and bot_token, and if bot_token is not provided and SLACK_BOT_TOKEN is a dict,
read a fallback SLACK_TEAM_ID env var (env("SLACK_TEAM_ID")) to pick the token
from SLACK_BOT_TOKEN[team_id]; if the fallback is missing or token lookup fails,
keep raising the ValueError as before. Ensure callers like SlackFetcher.__init__
and any no-arg get_slack_client() usage can rely on this environment fallback so
single-workspace setups work without changing every call site.

In `@operations/slack_ops/tokens.py`:
- Around line 17-53: The new strict requirement that
get_slack_bot_token(team_id) always receive a team_id breaks callers; change
get_slack_bot_token and its consumer get_slack_client to accept an optional
team_id and, when team_id is missing or empty, fall back to a default
SLACK_TEAM_ID from Django settings (getattr(settings, "SLACK_TEAM_ID", None))
before raising; update the lookup logic in get_slack_bot_token to use that
fallback, keep the same error logging and ValueError only if both team_id and
fallback are absent or the token is missing, and ensure callers that already
pass team_id continue to work unchanged (refer to get_slack_bot_token and
get_slack_client to locate code to modify).

---

Nitpick comments:
In `@cppa_slack_tracker/fetcher.py`:
- Around line 224-244: The message date-filtering block duplicates nearly
identical logic for created vs edited timestamps; refactor by extracting a small
helper (e.g., _in_range(date, start_date, end_date) or _check_and_append(msg,
timestamp_key)) and use it to evaluate both msg.get("ts") and
msg.get("edited")["ts"] for each msg, replacing the two branches that check
start_date is None vs bounded; keep using _ts_to_utc_date, all_messages,
start_date, end_date and ensure behavior remains: when start_date is None treat
check as date <= end_date, otherwise enforce start_date <= date <= end_date,
appending msg if either created or edited timestamp matches.

In `@cppa_slack_tracker/sync/sync_channel_user.py`:
- Around line 69-71: In sync_channel_user.py, inside the exception handling
where logger.exception is called (the block that logs "Failed to fetch/sync
members for %s: %s"), remove the redundant exception interpolation by passing
only the channel identifier (e.g., channel.channel_id) as the formatting
argument and let logger.exception capture the exception/traceback automatically;
update the logger.exception call used in the member sync/fetch function so it
reads a single-format-argument message referencing channel.channel_id (do not
include the exception object `e` in the format args).

In `@cppa_slack_tracker/sync/sync_message.py`:
- Around line 187-189: The conditional assignment setting start_date is
redundant; simply assign the result of _last_message_date(channel) directly to
start_date (e.g., start_date = _last_message_date(channel)) or set start_date =
last_d after calling _last_message_date(channel), removing the if-else
expression that checks for None; update the assignment near the
_last_message_date(channel) call and the start_date variable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 98e4d8e1-de90-4586-bccc-64adfc1c42c7

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 0e24c6f.

📒 Files selected for processing (13)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py

Comment thread config/settings.py
Comment thread operations/slack_ops/tokens.py
@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
cppa_slack_transcript_tracker/runner.py (1)

34-38: Team-scoped token resolution works correctly, though explicit lookup is redundant.

The code explicitly reads SLACK_TEAM_ID and passes it to get_slack_bot_token(team_id=team_id). However, get_slack_bot_token() already has internal fallback logic via _slack_team_id_fallback() that performs the same lookup when team_id is None.

This redundancy is harmless and makes the intent clearer, so no change is required. The ValueError catch correctly handles missing token scenarios.

♻️ Optional: Simplify by relying on internal fallback
     try:
-        team_id = getattr(settings, "SLACK_TEAM_ID", None) or None
-        bot_token = bot_token or get_slack_bot_token(team_id=team_id)
+        bot_token = bot_token or get_slack_bot_token()
     except ValueError:
         bot_token = None

The internal fallback in get_slack_bot_token() already reads SLACK_TEAM_ID from settings and os.environ.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_transcript_tracker/runner.py` around lines 34 - 38, The current
code redundantly reads SLACK_TEAM_ID via getattr(settings, "SLACK_TEAM_ID",
None) before calling get_slack_bot_token(team_id=team_id); simplify by letting
get_slack_bot_token() perform its internal fallback: call get_slack_bot_token()
without passing an explicit team_id (or pass None) and keep the existing
ValueError exception handling so bot_token is set to None on missing token;
update the try/except block around get_slack_bot_token() (referencing
get_slack_bot_token and SLACK_TEAM_ID) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py`:
- Around line 149-151: Update the stale dry-run/default messaging in
run_cppa_slack_tracker.py to reflect that the default execution runs users,
channels and messages (not "sync messages only"); change any help text, comments
or log messages that say "sync messages only (default)" to something like "sync
users, channels and messages (default)" and ensure any dry-run output that
prints the default path references the three actions invoked by sync_users,
sync_channels and sync_messages so messaging matches the actual behavior.

In `@cppa_slack_transcript_tracker/utils/slack_listener.py`:
- Around line 55-56: The code in slack_listener.py reads Django settings
unconditionally when computing team_id before honoring an explicit bot_token,
which can raise configuration errors in non-Django runtimes; change the logic in
the SlackListener initializer (where self.bot_token is set and team_id is
currently computed) to first check if bot_token was provided and if so skip any
access to django.conf.settings, otherwise only then call getattr(settings,
"SLACK_TEAM_ID", None) and get_slack_bot_token(team_id=team_id) to derive
self.bot_token.

---

Nitpick comments:
In `@cppa_slack_transcript_tracker/runner.py`:
- Around line 34-38: The current code redundantly reads SLACK_TEAM_ID via
getattr(settings, "SLACK_TEAM_ID", None) before calling
get_slack_bot_token(team_id=team_id); simplify by letting get_slack_bot_token()
perform its internal fallback: call get_slack_bot_token() without passing an
explicit team_id (or pass None) and keep the existing ValueError exception
handling so bot_token is set to None on missing token; update the try/except
block around get_slack_bot_token() (referencing get_slack_bot_token and
SLACK_TEAM_ID) accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a703599a-71bb-4206-ab0f-415a809c0879

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 1b777e9.

📒 Files selected for processing (16)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py

Comment thread cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
Comment thread cppa_slack_transcript_tracker/utils/slack_listener.py Outdated
@leostar0412
Copy link
Copy Markdown
Collaborator Author

@CodeRabbit full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py (1)

130-133: ⚠️ Potential issue | 🟡 Minor

Update stale default-path messaging to match actual behavior.

Default execution now runs users + channels + messages, but comments and dry-run output still say “sync messages only (default)”.

🔧 Suggested fix
-        # when set; multiple flags can be combined. If none are set, default to
-        # syncing messages only.
+        # when set; multiple flags can be combined. If none are set, default to
+        # syncing users, channels, and messages.
@@
-        self.stdout.write("  Would run: sync messages only (default)")
+        self.stdout.write("  Would run: sync users, channels, and messages (default)")

Also applies to: 189-194

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py` around
lines 130 - 133, The inline comment and dry-run messaging in
run_cppa_slack_tracker.py are out of date: update the comment block that
currently states the default is "sync messages only (default)" to reflect the
new default behavior of running users + channels + messages, and adjust any
dry-run output text emitted by the Command class (e.g., in Command.handle or any
helper method that prints the default-run summary) to say "default: sync users,
channels, and messages" (or equivalent) so comments and runtime messages match
actual behavior.
cppa_slack_transcript_tracker/utils/slack_listener.py (1)

54-56: ⚠️ Potential issue | 🟠 Major

Honor explicit bot_token before reading Django settings.

team_id is resolved unconditionally, so explicit token usage still depends on settings access. Short-circuit on bot_token first.

🔧 Suggested fix
 def __init__(self, bot_token=None, app_token=None):
-    team_id = getattr(settings, "SLACK_TEAM_ID", None) or None
-    self.bot_token = bot_token or get_slack_bot_token(team_id=team_id)
+    if bot_token:
+        self.bot_token = bot_token
+    else:
+        try:
+            team_id = (getattr(settings, "SLACK_TEAM_ID", None) or "").strip() or None
+        except Exception:
+            team_id = None
+        self.bot_token = get_slack_bot_token(team_id=team_id)
In Django 4.2, can `getattr(django.conf.settings, "SOME_KEY", None)` raise `ImproperlyConfigured` when settings are not configured?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_transcript_tracker/utils/slack_listener.py` around lines 54 - 56,
In __init__ short-circuit when an explicit bot_token is provided: if bot_token
is truthy set self.bot_token to it and avoid touching Django settings; otherwise
resolve team_id from settings.SLACK_TEAM_ID and call get_slack_bot_token to
populate self.bot_token. This ensures __init__ (and the get_slack_bot_token
call) only accesses settings when necessary and honors an explicit bot_token.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py`:
- Around line 130-133: The inline comment and dry-run messaging in
run_cppa_slack_tracker.py are out of date: update the comment block that
currently states the default is "sync messages only (default)" to reflect the
new default behavior of running users + channels + messages, and adjust any
dry-run output text emitted by the Command class (e.g., in Command.handle or any
helper method that prints the default-run summary) to say "default: sync users,
channels, and messages" (or equivalent) so comments and runtime messages match
actual behavior.

In `@cppa_slack_transcript_tracker/utils/slack_listener.py`:
- Around line 54-56: In __init__ short-circuit when an explicit bot_token is
provided: if bot_token is truthy set self.bot_token to it and avoid touching
Django settings; otherwise resolve team_id from settings.SLACK_TEAM_ID and call
get_slack_bot_token to populate self.bot_token. This ensures __init__ (and the
get_slack_bot_token call) only accesses settings when necessary and honors an
explicit bot_token.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4d5d7e71-ad25-4199-bb25-5e5e3ed74b0f

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 1b777e9.

📒 Files selected for processing (16)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py

@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

1 similar comment
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cppa_slack_tracker/fetcher.py (1)

197-244: ⚠️ Potential issue | 🟠 Major

Avoid buffering full channel history before date filtering.

fetch_messages stores every page in all_messages and only filters afterward. With start_date=None, this can become unbounded memory growth on large channels.

💡 Proposed fix (filter per page while paginating)
-    all_messages = []
+    messages = []
     cursor = None
     while True:
         kwargs = {
             "channel": channel_id,
             "limit": 1000,
             "latest": latest_ts,
             "cursor": cursor,
         }
         if oldest_ts is not None:
             kwargs["oldest"] = oldest_ts
         data = client.conversations_history(**kwargs)
         if not data.get("ok"):
             logger.warning(
                 "conversations.history failed for %s: %s",
                 channel_id,
                 data.get("error", "unknown"),
             )
             break
         batch = data.get("messages", [])
-        all_messages.extend(batch)
+        for msg in batch:
+            created_d = _ts_to_utc_date(msg.get("ts"))
+            if start_date is not None:
+                if created_d and start_date <= created_d <= end_date:
+                    messages.append(msg)
+                    continue
+                edited = msg.get("edited") or {}
+                edited_d = _ts_to_utc_date(edited.get("ts"))
+                if edited_d and start_date <= edited_d <= end_date:
+                    messages.append(msg)
+            else:
+                if created_d and created_d <= end_date:
+                    messages.append(msg)
+                    continue
+                edited = msg.get("edited") or {}
+                edited_d = _ts_to_utc_date(edited.get("ts"))
+                if edited_d and edited_d <= end_date:
+                    messages.append(msg)
         if not batch:
             break
         cursor = (data.get("response_metadata") or {}).get("next_cursor")
         if not cursor:
             break
-
-    # Filter by date: [start_date, end_date] or <= end_date when start_date is None
-    messages = []
-    for msg in all_messages:
-        created_d = _ts_to_utc_date(msg.get("ts"))
-        if start_date is not None:
-            if created_d and start_date <= created_d <= end_date:
-                messages.append(msg)
-                continue
-            edited = msg.get("edited") or {}
-            edited_d = _ts_to_utc_date(edited.get("ts"))
-            if edited_d and start_date <= edited_d <= end_date:
-                messages.append(msg)
-        else:
-            if created_d and created_d <= end_date:
-                messages.append(msg)
-                continue
-            edited = msg.get("edited") or {}
-            edited_d = _ts_to_utc_date(edited.get("ts"))
-            if edited_d and edited_d <= end_date:
-                messages.append(msg)
     return messages
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/fetcher.py` around lines 197 - 244, The function
fetch_messages currently accumulates every page into all_messages before date
filtering, risking unbounded memory usage for large channels; change the
pagination loop to filter each batch as it is received (using _ts_to_utc_date on
msg["ts"] and msg.get("edited")["ts"]) and append only messages that meet the
existing date logic to a persisted messages list (replace uses of all_messages
with per-batch filtering), preserving the same start_date None behavior and the
existing pagination/cursor handling (variables: client.conversations_history,
kwargs, batch, cursor, start_date, end_date, _ts_to_utc_date).
🧹 Nitpick comments (2)
cppa_slack_tracker/sync/sync_message.py (1)

187-189: Minor: Simplify redundant conditional assignment.

Line 189 can be simplified since last_d is already Optional[date] and the conditional is effectively a no-op.

♻️ Proposed simplification
         # Determine start_date: same day as last message, or None to fetch from beginning
         last_d = _last_message_date(channel)
-        start_date = last_d if last_d is not None else None
+        start_date = last_d
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_message.py` around lines 187 - 189, The
assignment to start_date is redundant because _last_message_date(channel)
already returns Optional[date]; replace the two-step last_d then conditional
assignment with a single direct assignment by calling
_last_message_date(channel) and assigning its result to start_date (use the
existing symbol _last_message_date and variable start_date in sync_message.py).
cppa_slack_tracker/sync/sync_channel_user.py (1)

68-72: Remove redundant exception object from logging.exception call.

The static analysis tool correctly flags that e is redundant in logger.exception() since the exception info is automatically included.

♻️ Proposed fix
         except Exception as e:
             logger.exception(
-                "Failed to fetch/sync members for %s: %s", channel.channel_id, e
+                "Failed to fetch/sync members for %s", channel.channel_id
             )
             error_count += 1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_channel_user.py` around lines 68 - 72, The
logger.exception call passes the caught exception object redundantly; update the
call in the except block that handles member sync for the channel (the
logger.exception call referencing channel.channel_id and variable e) to omit e
and only pass the formatted arguments (e.g., logger.exception("Failed to
fetch/sync members for %s", channel.channel_id)), leaving error_count += 1
unchanged so exception info is still captured automatically.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@cppa_slack_tracker/fetcher.py`:
- Around line 197-244: The function fetch_messages currently accumulates every
page into all_messages before date filtering, risking unbounded memory usage for
large channels; change the pagination loop to filter each batch as it is
received (using _ts_to_utc_date on msg["ts"] and msg.get("edited")["ts"]) and
append only messages that meet the existing date logic to a persisted messages
list (replace uses of all_messages with per-batch filtering), preserving the
same start_date None behavior and the existing pagination/cursor handling
(variables: client.conversations_history, kwargs, batch, cursor, start_date,
end_date, _ts_to_utc_date).

---

Nitpick comments:
In `@cppa_slack_tracker/sync/sync_channel_user.py`:
- Around line 68-72: The logger.exception call passes the caught exception
object redundantly; update the call in the except block that handles member sync
for the channel (the logger.exception call referencing channel.channel_id and
variable e) to omit e and only pass the formatted arguments (e.g.,
logger.exception("Failed to fetch/sync members for %s", channel.channel_id)),
leaving error_count += 1 unchanged so exception info is still captured
automatically.

In `@cppa_slack_tracker/sync/sync_message.py`:
- Around line 187-189: The assignment to start_date is redundant because
_last_message_date(channel) already returns Optional[date]; replace the two-step
last_d then conditional assignment with a single direct assignment by calling
_last_message_date(channel) and assigning its result to start_date (use the
existing symbol _last_message_date and variable start_date in sync_message.py).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ab71d55d-b2bc-4b39-9a15-906b2584cf86

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 016dc30.

📒 Files selected for processing (16)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
cppa_slack_tracker/sync/sync_channel_user.py (1)

68-71: Redundant exception object in logging.exception call.

The e parameter is redundant since logger.exception() automatically includes the exception traceback. This is flagged by the static analysis hint (TRY401).

♻️ Suggested fix
         except Exception as e:
             logger.exception(
-                "Failed to fetch/sync members for %s: %s", channel.channel_id, e
+                "Failed to fetch/sync members for %s", channel.channel_id
             )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_channel_user.py` around lines 68 - 71, The
logger.exception call in sync_channel_user.py is passing the caught exception
object `e` redundantly; update the except block that surrounds the member sync
(the logger.exception call referencing `channel.channel_id`) to remove the `e`
argument and only log the channel id (e.g. logger.exception("Failed to
fetch/sync members for %s", channel.channel_id)) so the traceback is included
automatically by logger.exception.
cppa_slack_tracker/fetcher.py (1)

224-244: Consider consolidating the filter logic to reduce duplication.

The date filtering logic has two branches that are structurally similar. You could simplify this by using a helper or consolidating the condition.

♻️ Suggested consolidation
     # Filter by date: [start_date, end_date] or <= end_date when start_date is None
     messages = []
     for msg in all_messages:
         created_d = _ts_to_utc_date(msg.get("ts"))
-        if start_date is not None:
-            if created_d and start_date <= created_d <= end_date:
-                messages.append(msg)
-                continue
-            edited = msg.get("edited") or {}
-            edited_d = _ts_to_utc_date(edited.get("ts"))
-            if edited_d and start_date <= edited_d <= end_date:
-                messages.append(msg)
-        else:
-            if created_d and created_d <= end_date:
-                messages.append(msg)
-                continue
-            edited = msg.get("edited") or {}
-            edited_d = _ts_to_utc_date(edited.get("ts"))
-            if edited_d and edited_d <= end_date:
-                messages.append(msg)
+        edited = msg.get("edited") or {}
+        edited_d = _ts_to_utc_date(edited.get("ts"))
+        
+        def in_range(d: date | None) -> bool:
+            if d is None:
+                return False
+            if start_date is not None:
+                return start_date <= d <= end_date
+            return d <= end_date
+        
+        if in_range(created_d) or in_range(edited_d):
+            messages.append(msg)
     return messages
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/fetcher.py` around lines 224 - 244, The date-filtering
branches duplicate logic; create a small helper (e.g., def _in_range(d,
start_date, end_date): return d and (start_date is None and d <= end_date or
start_date <= d <= end_date)) and replace the two branches by a single loop over
all_messages where you compute created_d = _ts_to_utc_date(msg.get("ts")) and
edited_d = _ts_to_utc_date((msg.get("edited") or {}).get("ts")) and append msg
if _in_range(created_d, start_date, end_date) or _in_range(edited_d, start_date,
end_date). This keeps the existing symbols (all_messages, start_date, end_date,
_ts_to_utc_date) and preserves behavior while removing duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@cppa_slack_tracker/fetcher.py`:
- Around line 224-244: The date-filtering branches duplicate logic; create a
small helper (e.g., def _in_range(d, start_date, end_date): return d and
(start_date is None and d <= end_date or start_date <= d <= end_date)) and
replace the two branches by a single loop over all_messages where you compute
created_d = _ts_to_utc_date(msg.get("ts")) and edited_d =
_ts_to_utc_date((msg.get("edited") or {}).get("ts")) and append msg if
_in_range(created_d, start_date, end_date) or _in_range(edited_d, start_date,
end_date). This keeps the existing symbols (all_messages, start_date, end_date,
_ts_to_utc_date) and preserves behavior while removing duplication.

In `@cppa_slack_tracker/sync/sync_channel_user.py`:
- Around line 68-71: The logger.exception call in sync_channel_user.py is
passing the caught exception object `e` redundantly; update the except block
that surrounds the member sync (the logger.exception call referencing
`channel.channel_id`) to remove the `e` argument and only log the channel id
(e.g. logger.exception("Failed to fetch/sync members for %s",
channel.channel_id)) so the traceback is included automatically by
logger.exception.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cb6c225a-d198-4db5-8c26-052a0353fca9

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 016dc30.

📒 Files selected for processing (16)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py

@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review resume

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

@leostar0412: Sure! I'll resume automatic reviews for this PR.

✅ Actions performed

Reviews resumed.

@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
config/settings.py (1)

181-188: Warn when a configured team ID has no matching token env var.

Teams listed in SLACK_TEAM_IDS but missing SLACK_BOT_TOKEN_<team_id> are currently dropped silently, which can hide misconfiguration until runtime token lookup. Consider logging a startup warning for each missing token key.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@config/settings.py` around lines 181 - 188, The loop over ids_raw that builds
out the out dict for SLACK_TEAM_IDS should emit a startup warning when a team id
has no corresponding SLACK_BOT_TOKEN_<team_id> env var instead of silently
dropping it; modify the block where key = f"SLACK_BOT_TOKEN_{tid}" and token =
(env(key, default="") or "").strip() to check if token is empty and call the
module/application logger (or warnings.warn) to log a clear warning referencing
the missing key and tid before continuing so operators can detect
misconfiguration at startup.
cppa_slack_tracker/sync/sync_message.py (1)

187-189: Consider documenting the full-history fetch behavior.

When start_date remains None (no DB messages), the API is called without an oldest parameter, potentially fetching the entire channel history. While this is the intended design per the docstring, consider whether this could cause rate-limiting or performance issues for channels with extensive history.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_message.py` around lines 187 - 189, The code
sets start_date = _last_message_date(channel) or None which, when None, causes
the API call to fetch the channel's full history; update the function docstring
that contains this logic (and any public function that calls it, e.g., the sync
routine around _last_message_date and start_date) to explicitly document that
start_date == None triggers a full-history fetch, note potential
performance/rate-limit implications, and add an inline comment next to the
start_date assignment; optionally add a safety/logging measure (e.g., emit a
warning via logger or enforce a configurable max_lookback) if full-history fetch
is used so callers are aware and can opt into/avoid it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@config/settings.py`:
- Around line 181-188: The loop over ids_raw that builds out the out dict for
SLACK_TEAM_IDS should emit a startup warning when a team id has no corresponding
SLACK_BOT_TOKEN_<team_id> env var instead of silently dropping it; modify the
block where key = f"SLACK_BOT_TOKEN_{tid}" and token = (env(key, default="") or
"").strip() to check if token is empty and call the module/application logger
(or warnings.warn) to log a clear warning referencing the missing key and tid
before continuing so operators can detect misconfiguration at startup.

In `@cppa_slack_tracker/sync/sync_message.py`:
- Around line 187-189: The code sets start_date = _last_message_date(channel) or
None which, when None, causes the API call to fetch the channel's full history;
update the function docstring that contains this logic (and any public function
that calls it, e.g., the sync routine around _last_message_date and start_date)
to explicitly document that start_date == None triggers a full-history fetch,
note potential performance/rate-limit implications, and add an inline comment
next to the start_date assignment; optionally add a safety/logging measure
(e.g., emit a warning via logger or enforce a configurable max_lookback) if
full-history fetch is used so callers are aware and can opt into/avoid it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9c0e88ab-e5f5-49b2-b98c-433f2326bdd0

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 2ba9d41.

📒 Files selected for processing (16)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py

@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
cppa_slack_transcript_tracker/runner.py (1)

35-36: ⚠️ Potential issue | 🟠 Major

Honor explicit bot_token before touching Django settings.

At Line 35, settings are accessed unconditionally, even when bot_token is already provided. That can fail in non-Django runtime contexts and block valid manual token usage.

In Django, can `getattr(django.conf.settings, "SOME_KEY", None)` raise `ImproperlyConfigured` when settings are not configured?
🔧 Proposed fix
-    try:
-        team_id = getattr(settings, "SLACK_TEAM_ID", None) or None
-        bot_token = bot_token or get_slack_bot_token(team_id=team_id)
-    except ValueError:
-        bot_token = None
+    try:
+        if bot_token:
+            resolved_bot_token = bot_token
+        else:
+            try:
+                team_id = (
+                    getattr(settings, "SLACK_TEAM_ID", None) or ""
+                ).strip() or None
+            except Exception:
+                team_id = None
+            resolved_bot_token = get_slack_bot_token(team_id=team_id)
+        bot_token = resolved_bot_token
+    except ValueError:
+        bot_token = None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_transcript_tracker/runner.py` around lines 35 - 36, The code
unconditionally accesses Django settings via getattr(settings, "SLACK_TEAM_ID",
None) even when an explicit bot_token is provided; change the logic in runner.py
so you first check if bot_token is truthy and return/use it as-is, and only call
getattr(settings, "SLACK_TEAM_ID", None) and
get_slack_bot_token(team_id=team_id) when bot_token is falsy/None; update the
flow around the variables team_id, bot_token and the call to get_slack_bot_token
to avoid touching settings when an explicit bot_token is supplied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cppa_slack_tracker/sync/sync_channel.py`:
- Around line 81-83: The call to the Slack SDK method is using a positional arg
and will raise because conversations_info requires keyword-only parameters;
update the call in sync_channel.py where you call
get_slack_client(team_id=tid).conversations_info(channel_id) to use
conversations_info(channel=channel_id) so the method receives the channel
parameter correctly (keep get_slack_client and the tid/channel_id variables
unchanged).

In `@cppa_slack_tracker/sync/sync_message.py`:
- Around line 136-148: The merge function _merge_messages_by_ts currently uses
raw m.get("ts") for dict keys and sorting which can mix types and mis-order
values; normalize ts by coercing msg["ts"] to a consistent string key when
populating by_ts (e.g., str(msg["ts"])) and use a numeric sort key (e.g.,
float(ts) or int(...) fallback) when returning the sorted list so ordering is
numeric and stable; update code paths involving by_ts, msg and the final
sorted(..., key=...) accordingly and handle missing/invalid ts by falling back
to 0 or "" consistently.

---

Duplicate comments:
In `@cppa_slack_transcript_tracker/runner.py`:
- Around line 35-36: The code unconditionally accesses Django settings via
getattr(settings, "SLACK_TEAM_ID", None) even when an explicit bot_token is
provided; change the logic in runner.py so you first check if bot_token is
truthy and return/use it as-is, and only call getattr(settings, "SLACK_TEAM_ID",
None) and get_slack_bot_token(team_id=team_id) when bot_token is falsy/None;
update the flow around the variables team_id, bot_token and the call to
get_slack_bot_token to avoid touching settings when an explicit bot_token is
supplied.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e68d32d5-6a09-49ed-a841-0eb49d4e0d24

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and de3b743.

📒 Files selected for processing (17)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_services.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py
💤 Files with no reviewable changes (1)
  • cppa_slack_tracker/tests/test_services.py

Comment thread cppa_slack_tracker/sync/sync_channel.py
Comment thread cppa_slack_tracker/sync/sync_message.py
@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cppa_slack_tracker/sync/sync_user.py (1)

39-55: ⚠️ Potential issue | 🟠 Major

team_slug fallback can break API token resolution.

At Line 54, fetch_user_list(team_id or team_slug) passes team_slug as _team_id when team_id is missing. For API/token-scoped fetches, that value should be the Slack workspace ID, not a slug/name. This can cause avoidable sync failures.

Proposed patch
 def sync_users(
     team_slug: str,
     *,
     team_id: Optional[str] = None,
     include_bots: bool = True,
 ) -> tuple[int, int]:
@@
-    try:
-        members = fetch_user_list(team_id or team_slug)
+    tid = (team_id or "").strip()
+    if not tid:
+        logger.error("team_id is required for sync_users (team_slug=%s)", team_slug)
+        return success_count, error_count + 1
+
+    try:
+        members = fetch_user_list(tid)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_user.py` around lines 39 - 55, The code calls
fetch_user_list(team_id or team_slug) which passes a slug into the _team_id
parameter when team_id is None; change the call to pass explicit keyword args so
the resolver gets the correct parameter: call fetch_user_list(team_id=team_id)
when team_id is set, otherwise call fetch_user_list(team_slug=team_slug). Update
the call site in the sync_users function (variables: team_id, team_slug, and
fetch_user_list) so API token-scoped fetches receive the actual workspace ID
instead of a slug.
🧹 Nitpick comments (1)
cppa_slack_transcript_tracker/runner.py (1)

35-38: Keep the token resolution error detail in logs.

At Line 37, ValueError is swallowed and only a generic later message is emitted. Logging the exception with team_id will make misconfiguration debugging much faster.

Proposed patch
-    except ValueError:
+    except ValueError as e:
+        logger.error(
+            "Failed to resolve Slack bot token for team_id=%r: %s",
+            team_id,
+            e,
+        )
         bot_token = None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_transcript_tracker/runner.py` around lines 35 - 38, When resolving
bot_token (the call to get_slack_bot_token using team_id derived from
settings.SLACK_TEAM_ID), don’t swallow the ValueError: update the except
ValueError block to log the exception details and the team_id (e.g., via
logger.exception or logging.exception) before setting bot_token = None so token
resolution errors include the team_id and the original error for debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cppa_slack_transcript_tracker/utils/slack_listener.py`:
- Around line 55-65: Normalize the incoming bot_token by trimming whitespace
before the truthy check so whitespace-only strings don't bypass fallback
resolution: in the Slack listener constructor (where self.bot_token is set) call
bot_token = bot_token.strip() (or equivalent) and then if bot_token: set
self.bot_token = bot_token else resolve team_id and call
get_slack_bot_token(team_id=team_id); leave self.app_token assignment unchanged.
Ensure any exception handling for settings.SLACK_TEAM_ID remains intact and that
self.bot_token is never set to a whitespace-only string.

In `@operations/slack_ops/tokens.py`:
- Around line 48-64: The get_slack_bot_token path currently assumes Django
settings exist and treats failure to import settings as tokens_map=None, causing
a premature error even when SLACK_BOT_TOKEN_<tid> is present in the environment;
update the logic in operations/slack_ops/tokens.py (around the tokens_map
handling and the get_slack_bot_token flow) so that if importing
django.conf.settings fails or settings.SLACK_BOT_TOKEN is not a dict, you fall
back to checking os.environ for a key named SLACK_BOT_TOKEN_<tid> (and only
raise ValueError if neither settings.SLACK_BOT_TOKEN[tid] nor the corresponding
environment variable is found), keeping the existing logger.error message when
raising and referencing tid, tokens_map, and logger to locate the exact spots to
change.

---

Outside diff comments:
In `@cppa_slack_tracker/sync/sync_user.py`:
- Around line 39-55: The code calls fetch_user_list(team_id or team_slug) which
passes a slug into the _team_id parameter when team_id is None; change the call
to pass explicit keyword args so the resolver gets the correct parameter: call
fetch_user_list(team_id=team_id) when team_id is set, otherwise call
fetch_user_list(team_slug=team_slug). Update the call site in the sync_users
function (variables: team_id, team_slug, and fetch_user_list) so API
token-scoped fetches receive the actual workspace ID instead of a slug.

---

Nitpick comments:
In `@cppa_slack_transcript_tracker/runner.py`:
- Around line 35-38: When resolving bot_token (the call to get_slack_bot_token
using team_id derived from settings.SLACK_TEAM_ID), don’t swallow the
ValueError: update the except ValueError block to log the exception details and
the team_id (e.g., via logger.exception or logging.exception) before setting
bot_token = None so token resolution errors include the team_id and the original
error for debugging.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cec89ea1-bac9-4411-b94f-864ec7c6dae0

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 97fd885.

📒 Files selected for processing (17)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_services.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py
💤 Files with no reviewable changes (1)
  • cppa_slack_tracker/tests/test_services.py

Comment thread cppa_slack_transcript_tracker/utils/slack_listener.py Outdated
Comment thread operations/slack_ops/tokens.py
@leostar0412
Copy link
Copy Markdown
Collaborator Author

@CodeRabbit full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cppa_slack_tracker/fetcher.py (1)

243-274: ⚠️ Potential issue | 🔴 Critical

Prevent destructive membership updates when conversations.members fails.

At Line 263, API failures break and return partial/empty member IDs. Downstream membership sync can treat that as authoritative and incorrectly soft-delete valid channel memberships. This should fail fast instead of returning degraded data.

🛠️ Proposed fix
 def fetch_channel_user_list(
@@
     while True:
         data = client.conversations_members(
             channel=channel_id,
             limit=1000,
             cursor=cursor,
         )
         if not data.get("ok"):
-            logger.warning(
-                "conversations.members failed for %s: %s",
-                channel_id,
-                data.get("error", "unknown"),
-            )
-            break
+            error = data.get("error", "unknown")
+            logger.warning("conversations.members failed for %s: %s", channel_id, error)
+            raise RuntimeError(
+                f"conversations.members failed for channel_id={channel_id}: {error}"
+            )
         user_ids.extend(data.get("members", []))
         cursor = (data.get("response_metadata") or {}).get("next_cursor")
         if not cursor:
             break
     return user_ids
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/fetcher.py` around lines 243 - 274,
fetch_channel_user_list currently stops on a failed client.conversations_members
call and returns whatever partial user_ids were collected, which can cause
destructive downstream syncs; change the failure path in fetch_channel_user_list
so that when data.get("ok") is false you raise an exception (e.g., RuntimeError
or a Slack-specific error) including the channel_id and
data.get("error")/response_metadata instead of breaking and returning partial
results, ensuring no partial user_ids are returned to callers.
♻️ Duplicate comments (1)
cppa_slack_transcript_tracker/utils/slack_listener.py (1)

55-65: ⚠️ Potential issue | 🟡 Minor

Trim explicit tokens before truthiness checks.

At Line 55, whitespace-only bot_token still bypasses fallback resolution; same risk applies to app_token at Line 65.

🔧 Proposed fix
-        if bot_token:
-            self.bot_token = bot_token
+        token = (bot_token or "").strip()
+        if token:
+            self.bot_token = token
         else:
             try:
                 team_id = (
                     getattr(settings, "SLACK_TEAM_ID", None) or ""
                 ).strip() or None
             except Exception:
                 team_id = None
             self.bot_token = get_slack_bot_token(team_id=team_id)
-        self.app_token = app_token or get_slack_app_token()
+        self.app_token = (app_token or "").strip() or get_slack_app_token()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_transcript_tracker/utils/slack_listener.py` around lines 55 - 65,
The code uses raw bot_token and app_token truthiness so whitespace-only strings
bypass fallback; update the SlackListener initialization to trim and validate
tokens before checking truthiness: for bot_token, call strip() (or normalize via
token = bot_token.strip() if bot_token is not None and bot_token.strip() else
None) and only use the provided value when non-empty, otherwise call
get_slack_bot_token(team_id=...) (while deriving team_id from
settings.SLACK_TEAM_ID as already done); do the same for app_token (use
app_token.strip() and fallback to get_slack_app_token() when empty) so
get_slack_bot_token and get_slack_app_token are only used when appropriate.
🧹 Nitpick comments (4)
cppa_slack_tracker/sync/sync_user.py (1)

53-55: Avoid using team_slug as a fallback for Slack API team resolution.

Line 54 mixes slug/name fallback into a team-id API path; this can hide missing team_id and fail token lookup indirectly. Prefer failing fast when team_id is absent.

♻️ Proposed refactor
-    try:
-        members = fetch_user_list(team_id or team_slug)
+    resolved_team_id = (team_id or "").strip()
+    if not resolved_team_id:
+        logger.error(
+            "team_id is required to sync users via Slack API (team_slug=%s)",
+            team_slug,
+        )
+        return success_count, error_count + 1
+    try:
+        members = fetch_user_list(resolved_team_id)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_user.py` around lines 53 - 55, The code is
incorrectly falling back to team_slug when calling fetch_user_list (mixing a
slug into a team-id API path); change the logic in sync_user.py so
fetch_user_list is only ever called with a valid team_id: validate team_id
before the try/except, fail fast (raise or return an explicit error) if team_id
is missing instead of using team_slug, and remove the fallback usage in the
fetch_user_list call; ensure any downstream token lookup uses the validated
team_id.
config/settings.py (1)

192-197: Validate default team ID against token map at startup.

SLACK_TEAM_ID can be set to a workspace that is not present in SLACK_BOT_TOKEN, which only fails later when client/token lookup runs. Consider emitting a startup warning (or raising) here for faster misconfiguration detection.

♻️ Proposed improvement
 SLACK_BOT_TOKEN = _slack_bot_token_from_env()

 SLACK_APP_TOKEN = (env("SLACK_APP_TOKEN", default="") or "").strip()
 # Optional: for cppa_slack_transcript_tracker (huddle transcript, token extraction)
 SLACK_TEAM_ID = (env("SLACK_TEAM_ID", default="") or "").strip()
+if SLACK_TEAM_ID and SLACK_TEAM_ID not in SLACK_BOT_TOKEN:
+    import logging
+    logging.getLogger(__name__).warning(
+        "SLACK_TEAM_ID=%s is not configured in SLACK_BOT_TOKEN map. "
+        "Set SLACK_TEAM_IDS and SLACK_BOT_TOKEN_%s.",
+        SLACK_TEAM_ID,
+        SLACK_TEAM_ID,
+    )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@config/settings.py` around lines 192 - 197, After reading SLACK_TEAM_ID,
validate it against the available bot-token mapping (the configured
SLACK_BOT_TOKEN value or the parsed token map created from environment) and emit
a clear startup warning or raise a ConfigurationError if the chosen
SLACK_TEAM_ID is not present; implement this check immediately after
SLACK_TEAM_ID is set in settings.py, referencing SLACK_TEAM_ID and
SLACK_BOT_TOKEN (or the token map variable/function that parses SLACK_BOT_TOKEN)
to confirm membership and log which team id and tokens were considered in the
message.
operations/slack_ops/tokens.py (1)

55-64: Separate “mapping missing” from “team missing” error paths.

Right now both cases emit the same “team_id missing from map” message, which is misleading when SLACK_BOT_TOKEN itself is missing/malformed. Splitting these branches will make misconfiguration debugging faster.

♻️ Proposed improvement
-    if not isinstance(tokens_map, dict) or tid not in tokens_map:
+    if not isinstance(tokens_map, dict):
+        logger.error(
+            "SLACK_BOT_TOKEN is not configured as a team_id -> token mapping."
+        )
+        raise ValueError(
+            "SLACK_BOT_TOKEN must be a dict-like mapping built from "
+            "SLACK_TEAM_IDS and SLACK_BOT_TOKEN_<team_id>."
+        )
+
+    if tid not in tokens_map:
         logger.error(
             "team_id %s is missing from SLACK_BOT_TOKEN. Set SLACK_TEAM_IDS and SLACK_BOT_TOKEN_%s in .env",
             tid,
             tid,
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@operations/slack_ops/tokens.py` around lines 55 - 64, The current check
conflates a missing/malformed SLACK_BOT_TOKEN mapping and a missing team id
because both branches log the same message; update the logic around tokens_map
and tid so there are two clear branches: first check "if not
isinstance(tokens_map, dict)" and log an error (via logger.error) indicating
SLACK_BOT_TOKEN is missing or malformed and instruct to set
SLACK_BOT_TOKEN_<TEAM> and SLACK_TEAM_IDS, then raise a ValueError with a
message about the SLACK_BOT_TOKEN map being invalid; else (i.e., tokens_map is a
dict) check "if tid not in tokens_map" and log a different error saying the
specific team_id is missing and how to add SLACK_BOT_TOKEN_{tid} and
SLACK_TEAM_IDS, and raise the existing ValueError referring to the missing
team_id; adjust messages around the variables tokens_map, tid, logger.error and
the raised ValueError to reflect these two distinct error paths.
cppa_slack_tracker/sync/sync_message.py (1)

198-229: Consider adding a guardrail for first-run full-history fetches.

At Line 200, start_date=None triggers a full-history API scan up to end_date. For large channels, this can be very expensive. Consider a configurable backfill window or checkpoint-based bootstrap mode to control first-run load.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_message.py` around lines 198 - 229, The current
logic lets start_date=None trigger a full-history fetch via
fetch_messages(channel_id, start_date, end_date, team_id=channel.team.team_id),
which can be extremely expensive on first run; add a guardrail before calling
fetch_messages: if start_date is None enforce a configurable backfill cap (e.g.,
MAX_BACKFILL_DAYS or BACKFILL_WINDOW) or switch to a checkpoint/bootstrap mode
that limits the range (compute a new start_date = max(end_date -
BACKFILL_WINDOW, some_minimum) or require an explicit flag to allow full
backfill), and surface/log when the cap is applied (include channel_id and
team_id) so that fetch_messages, the subsequent min_d/_ts_to_date logic, and
downstream processing operate on the bounded window instead of the entire
history.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cppa_slack_transcript_tracker/runner.py`:
- Around line 35-37: The code currently treats a whitespace-only explicit
bot_token as valid and thus skips fallback; update the normalization so that
before the fallback line (bot_token = bot_token or
get_slack_bot_token(team_id=team_id)) you strip and coerce empty/whitespace
strings to None (e.g., set bot_token = bot_token.strip() or None, then normalize
to None if empty) so get_slack_bot_token(team_id=team_id) will be called when an
explicit token is blank; apply this change around the bot_token variable in
runner.py where getattr(settings, "SLACK_TEAM_ID", None) and get_slack_bot_token
are used.

---

Outside diff comments:
In `@cppa_slack_tracker/fetcher.py`:
- Around line 243-274: fetch_channel_user_list currently stops on a failed
client.conversations_members call and returns whatever partial user_ids were
collected, which can cause destructive downstream syncs; change the failure path
in fetch_channel_user_list so that when data.get("ok") is false you raise an
exception (e.g., RuntimeError or a Slack-specific error) including the
channel_id and data.get("error")/response_metadata instead of breaking and
returning partial results, ensuring no partial user_ids are returned to callers.

---

Duplicate comments:
In `@cppa_slack_transcript_tracker/utils/slack_listener.py`:
- Around line 55-65: The code uses raw bot_token and app_token truthiness so
whitespace-only strings bypass fallback; update the SlackListener initialization
to trim and validate tokens before checking truthiness: for bot_token, call
strip() (or normalize via token = bot_token.strip() if bot_token is not None and
bot_token.strip() else None) and only use the provided value when non-empty,
otherwise call get_slack_bot_token(team_id=...) (while deriving team_id from
settings.SLACK_TEAM_ID as already done); do the same for app_token (use
app_token.strip() and fallback to get_slack_app_token() when empty) so
get_slack_bot_token and get_slack_app_token are only used when appropriate.

---

Nitpick comments:
In `@config/settings.py`:
- Around line 192-197: After reading SLACK_TEAM_ID, validate it against the
available bot-token mapping (the configured SLACK_BOT_TOKEN value or the parsed
token map created from environment) and emit a clear startup warning or raise a
ConfigurationError if the chosen SLACK_TEAM_ID is not present; implement this
check immediately after SLACK_TEAM_ID is set in settings.py, referencing
SLACK_TEAM_ID and SLACK_BOT_TOKEN (or the token map variable/function that
parses SLACK_BOT_TOKEN) to confirm membership and log which team id and tokens
were considered in the message.

In `@cppa_slack_tracker/sync/sync_message.py`:
- Around line 198-229: The current logic lets start_date=None trigger a
full-history fetch via fetch_messages(channel_id, start_date, end_date,
team_id=channel.team.team_id), which can be extremely expensive on first run;
add a guardrail before calling fetch_messages: if start_date is None enforce a
configurable backfill cap (e.g., MAX_BACKFILL_DAYS or BACKFILL_WINDOW) or switch
to a checkpoint/bootstrap mode that limits the range (compute a new start_date =
max(end_date - BACKFILL_WINDOW, some_minimum) or require an explicit flag to
allow full backfill), and surface/log when the cap is applied (include
channel_id and team_id) so that fetch_messages, the subsequent min_d/_ts_to_date
logic, and downstream processing operate on the bounded window instead of the
entire history.

In `@cppa_slack_tracker/sync/sync_user.py`:
- Around line 53-55: The code is incorrectly falling back to team_slug when
calling fetch_user_list (mixing a slug into a team-id API path); change the
logic in sync_user.py so fetch_user_list is only ever called with a valid
team_id: validate team_id before the try/except, fail fast (raise or return an
explicit error) if team_id is missing instead of using team_slug, and remove the
fallback usage in the fetch_user_list call; ensure any downstream token lookup
uses the validated team_id.

In `@operations/slack_ops/tokens.py`:
- Around line 55-64: The current check conflates a missing/malformed
SLACK_BOT_TOKEN mapping and a missing team id because both branches log the same
message; update the logic around tokens_map and tid so there are two clear
branches: first check "if not isinstance(tokens_map, dict)" and log an error
(via logger.error) indicating SLACK_BOT_TOKEN is missing or malformed and
instruct to set SLACK_BOT_TOKEN_<TEAM> and SLACK_TEAM_IDS, then raise a
ValueError with a message about the SLACK_BOT_TOKEN map being invalid; else
(i.e., tokens_map is a dict) check "if tid not in tokens_map" and log a
different error saying the specific team_id is missing and how to add
SLACK_BOT_TOKEN_{tid} and SLACK_TEAM_IDS, and raise the existing ValueError
referring to the missing team_id; adjust messages around the variables
tokens_map, tid, logger.error and the raised ValueError to reflect these two
distinct error paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a99de737-1d00-4d42-a0be-1f27907820d8

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 97fd885.

📒 Files selected for processing (17)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_services.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py
💤 Files with no reviewable changes (1)
  • cppa_slack_tracker/tests/test_services.py

Comment thread cppa_slack_transcript_tracker/runner.py Outdated
@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cppa_slack_transcript_tracker/utils/slack_listener.py`:
- Around line 59-64: Remove the broad try/except that masks Django configuration
errors around the SLACK_TEAM_ID lookup; instead let errors propagate by
replacing the try/except block with a direct assignment using the same
expression: set team_id = (getattr(settings, "SLACK_TEAM_ID", None) or
"").strip() or None so ImproperlyConfigured (or other exceptions) from
getattr(settings, ...) are not swallowed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dc586eae-74e0-4706-b3b2-4b8be948c0f1

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 55c1152.

📒 Files selected for processing (17)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_services.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py
💤 Files with no reviewable changes (1)
  • cppa_slack_tracker/tests/test_services.py

Comment thread cppa_slack_transcript_tracker/utils/slack_listener.py
@leostar0412
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 6, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cppa_slack_transcript_tracker/utils/slack_listener.py (1)

54-70: ⚠️ Potential issue | 🟡 Minor

Normalize explicit app_token too.

A whitespace-only app_token still bypasses get_slack_app_token() and the if not self.app_token guard, so the listener can be initialized with an invalid Socket Mode token.

💡 Proposed fix
     def __init__(self, bot_token=None, app_token=None):
         token = (bot_token or "").strip()
         if token:
             self.bot_token = token
         else:
             try:
                 team_id = (
                     getattr(settings, "SLACK_TEAM_ID", None) or ""
                 ).strip() or None
             except Exception:
                 team_id = None
             self.bot_token = get_slack_bot_token(team_id=team_id)
-        self.app_token = app_token or get_slack_app_token()
+        app_token = (app_token or "").strip()
+        self.app_token = app_token or get_slack_app_token()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_transcript_tracker/utils/slack_listener.py` around lines 54 - 70,
The constructor (__init__) currently treats a whitespace-only app_token as
valid; normalize app_token like bot_token by stripping whitespace and falling
back to get_slack_app_token() when the provided value is empty/whitespace.
Specifically, change the logic around self.app_token so you set token_app =
(app_token or "").strip() and if token_app assign self.app_token = token_app
else call get_slack_app_token(); keep the existing ValueError guard for
self.app_token unchanged. Use the existing symbols __init__, self.app_token, and
get_slack_app_token to locate and update the code.
🧹 Nitpick comments (2)
cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py (1)

33-34: Normalize team_id for consistency with other callers.

The runner.py (line 35) strips whitespace from SLACK_TEAM_ID before use, but this command does not. A whitespace-only value would bypass fallback and potentially cause issues downstream.

♻️ Proposed fix for consistency
-            team_id = getattr(settings, "SLACK_TEAM_ID", None) or None
+            team_id = (getattr(settings, "SLACK_TEAM_ID", None) or "").strip() or None
             bot_token = get_slack_bot_token(team_id=team_id)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py`
around lines 33 - 34, Normalize SLACK_TEAM_ID the same way as runner.py by
stripping whitespace and treating empty/whitespace-only strings as None before
calling get_slack_bot_token: replace the current team_id assignment (team_id =
getattr(settings, "SLACK_TEAM_ID", None) or None) with logic that reads
settings.SLACK_TEAM_ID, strips it (e.g., .strip()), and sets team_id to None if
the stripped result is empty; keep the call to
get_slack_bot_token(team_id=team_id) unchanged.
cppa_slack_tracker/sync/sync_user.py (1)

54-68: Minor logging inconsistency.

Line 60 uses tid (stripped) for the API call, but line 66 logs team_id (original parameter). If a whitespace-padded team_id is passed, the log would show the original value while tid (the stripped version) is actually used.

♻️ Proposed fix for consistent logging
         logger.exception(
             "Failed to fetch users for team_slug=%s team_id=%s",
             team_slug,
-            team_id,
+            tid,
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_user.py` around lines 54 - 68, The logs use the
unstripped parameter team_id while the code calls fetch_user_list with the
stripped tid; update the logging to reference tid for consistency. In the block
that checks tid and the except block around fetch_user_list(tid), change
logger.error and logger.exception messages to include tid (the stripped value)
instead of team_id so the logged ID matches the value used in fetch_user_list;
keep team_slug as-is. Ensure references to tid appear in both the "team_id is
required" error and the "Failed to fetch users" exception log.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cppa_slack_tracker/fetcher.py`:
- Around line 200-208: The Slack API call in fetch_messages is missing the
inclusive flag so messages exactly at oldest_ts are excluded; update the call in
fetch_messages to include inclusive=True in the kwargs passed to
client.conversations_history, and then modify the
SlackAPIClient.conversations_history method signature to accept an inclusive
parameter and forward it to the underlying Slack API request so the API call
uses the inclusive option. Ensure the parameter name is inclusive (boolean) and
thread it through both the caller (fetch_messages) and the implementation in
SlackAPIClient.conversations_history.

In `@cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py`:
- Around line 192-198: The dry-run logging in handle() currently omits the
presence of a supplied --messages-json even though the default (no flags) path
will call sync_messages() on a real run; update the dry-run logging block that
prints channel_id/start/end (the logger.info lines) to also detect
options.get("messages_json") and log that messages would be imported (e.g. "   
messages_json=%s") so the dry-run shows this side effect; locate the default
branch in run_cppa_slack_tracker.py where handle() decides "Would run: sync
users, channels, and messages (default)" and add the additional logger.info for
messages_json (or otherwise ensure the dry-run prints that sync_messages would
be invoked) without changing sync_messages() behavior.

---

Outside diff comments:
In `@cppa_slack_transcript_tracker/utils/slack_listener.py`:
- Around line 54-70: The constructor (__init__) currently treats a
whitespace-only app_token as valid; normalize app_token like bot_token by
stripping whitespace and falling back to get_slack_app_token() when the provided
value is empty/whitespace. Specifically, change the logic around self.app_token
so you set token_app = (app_token or "").strip() and if token_app assign
self.app_token = token_app else call get_slack_app_token(); keep the existing
ValueError guard for self.app_token unchanged. Use the existing symbols
__init__, self.app_token, and get_slack_app_token to locate and update the code.

---

Nitpick comments:
In `@cppa_slack_tracker/sync/sync_user.py`:
- Around line 54-68: The logs use the unstripped parameter team_id while the
code calls fetch_user_list with the stripped tid; update the logging to
reference tid for consistency. In the block that checks tid and the except block
around fetch_user_list(tid), change logger.error and logger.exception messages
to include tid (the stripped value) instead of team_id so the logged ID matches
the value used in fetch_user_list; keep team_slug as-is. Ensure references to
tid appear in both the "team_id is required" error and the "Failed to fetch
users" exception log.

In
`@cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py`:
- Around line 33-34: Normalize SLACK_TEAM_ID the same way as runner.py by
stripping whitespace and treating empty/whitespace-only strings as None before
calling get_slack_bot_token: replace the current team_id assignment (team_id =
getattr(settings, "SLACK_TEAM_ID", None) or None) with logic that reads
settings.SLACK_TEAM_ID, strips it (e.g., .strip()), and sets team_id to None if
the stripped result is empty; keep the call to
get_slack_bot_token(team_id=team_id) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4293706d-1958-40ce-910d-fe4de86d7ae2

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 55c1152.

📒 Files selected for processing (17)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_services.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py
💤 Files with no reviewable changes (1)
  • cppa_slack_tracker/tests/test_services.py

Comment thread cppa_slack_tracker/fetcher.py
Comment thread cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
@leostar0412 leostar0412 removed the request for review from jonathanMLDev March 6, 2026 03:15
@leostar0412
Copy link
Copy Markdown
Collaborator Author

@CodeRabbit full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 6, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
cppa_slack_tracker/sync/sync_channel_user.py (1)

68-72: Consider removing redundant exception object from logger.exception.

The e argument is redundant since logger.exception() automatically includes the exception traceback.

♻️ Proposed fix
         except Exception as e:
             logger.exception(
-                "Failed to fetch/sync members for %s: %s", channel.channel_id, e
+                "Failed to fetch/sync members for %s", channel.channel_id
             )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cppa_slack_tracker/sync/sync_channel_user.py` around lines 68 - 72, In the
except block inside sync_channel_user.py, remove the redundant exception
argument from the logger.exception call so the traceback is logged
automatically; change the call from logger.exception("Failed to fetch/sync
members for %s: %s", channel.channel_id, e) to logger.exception("Failed to
fetch/sync members for %s", channel.channel_id) and leave error_count += 1
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@cppa_slack_tracker/sync/sync_channel_user.py`:
- Around line 68-72: In the except block inside sync_channel_user.py, remove the
redundant exception argument from the logger.exception call so the traceback is
logged automatically; change the call from logger.exception("Failed to
fetch/sync members for %s: %s", channel.channel_id, e) to
logger.exception("Failed to fetch/sync members for %s", channel.channel_id) and
leave error_count += 1 unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: db0833da-f260-4954-bff9-6a49e0e19b38

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2e0ea and 1594856.

📒 Files selected for processing (17)
  • .env.example
  • config/settings.py
  • cppa_slack_tracker/fetcher.py
  • cppa_slack_tracker/management/commands/run_cppa_slack_tracker.py
  • cppa_slack_tracker/services.py
  • cppa_slack_tracker/sync/__init__.py
  • cppa_slack_tracker/sync/sync_channel.py
  • cppa_slack_tracker/sync/sync_channel_user.py
  • cppa_slack_tracker/sync/sync_message.py
  • cppa_slack_tracker/sync/sync_user.py
  • cppa_slack_tracker/tests/test_services.py
  • cppa_slack_tracker/tests/test_sync_message.py
  • cppa_slack_transcript_tracker/management/commands/run_cppa_slack_transcript_tracker.py
  • cppa_slack_transcript_tracker/runner.py
  • cppa_slack_transcript_tracker/utils/slack_listener.py
  • operations/slack_ops/tokens.py
  • operations/tests/test_slack_tokens.py
💤 Files with no reviewable changes (1)
  • cppa_slack_tracker/tests/test_services.py

Copy link
Copy Markdown
Collaborator

@jonathanMLDev jonathanMLDev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@snowfox1003 snowfox1003 merged commit 0962781 into cppalliance:develop Mar 9, 2026
3 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.

3 participants