"""Sync Slack channel list, members-in-channel, and user list to DB using loops (not recursion).""" from celery.utils.log import get_task_logger from sqlmodel import Session from app.managers.slack import ( fetch_conversations_list, fetch_conversations_members, fetch_users_list, ) from app.managers.team import get_team_by_name from app.models import ChannelList, MembersInChannel, UserList logger = get_task_logger(__name__) def sync_channel_list(session: Session, team_name: str) -> None: """Fetch channel list and members per channel from Slack, merge into DB.""" team = get_team_by_name(session, team_name) if not team: raise ValueError(f"Team not found: {team_name}") logger.info("[%s] Channel sync started", team_name) token = team.team_token cursor = "" total_channels = 0 total_members = 0 page = 0 while True: page += 1 resp = fetch_conversations_list(token, cursor) if not resp.get("ok"): logger.error("[%s] Failed to fetch channels page %d: %s", team_name, page, resp.get("error")) break channels = resp.get("channels", []) for c in channels: total_members += _sync_members_in_channel(session, token, c["id"]) session.merge(ChannelList( channel_id=c["id"], channel_name=c.get("name"), team_name=team_name, )) session.commit() total_channels += len(channels) logger.info("[%s] Channel sync page %d: %d channels", team_name, page, len(channels)) cursor = (resp.get("response_metadata") or {}).get("next_cursor") if not cursor: break logger.info("[%s] Channel sync complete: %d channels, %d members across %d page(s)", team_name, total_channels, total_members, page) def _sync_members_in_channel(session: Session, token: str, channel_id: str) -> int: """Sync members for one channel, returns total member count synced.""" cursor = "" total = 0 while True: resp = fetch_conversations_members(token, channel_id, cursor) if not resp.get("ok"): logger.error("Failed to fetch members for channel %s: %s", channel_id, resp.get("error")) break members = resp.get("members", []) for m in members: session.merge(MembersInChannel(channel_id=channel_id, user_id=m)) session.commit() total += len(members) cursor = (resp.get("response_metadata") or {}).get("next_cursor") if not cursor: break return total def sync_user_list(session: Session, team_name: str) -> None: """Fetch user list from Slack, merge into DB.""" team = get_team_by_name(session, team_name) if not team: raise ValueError(f"Team not found: {team_name}") logger.info("[%s] User sync started", team_name) token = team.team_token cursor = "" total_synced = 0 total_skipped = 0 page = 0 while True: page += 1 resp = fetch_users_list(token, cursor) if not resp.get("ok"): logger.error("[%s] Failed to fetch users page %d: %s", team_name, page, resp.get("error")) break members = resp.get("members", []) page_synced = 0 page_skipped = 0 for i in members: profile = i.get("profile") or {} email = profile.get("email") if not email: page_skipped += 1 continue session.merge(UserList( user_id=i["id"], user_name=i.get("name"), tz=i.get("tz") or "", email=email, team_name=team_name, )) page_synced += 1 session.commit() total_synced += page_synced total_skipped += page_skipped logger.info("[%s] User sync page %d: %d synced, %d skipped (no email)", team_name, page, page_synced, page_skipped) cursor = (resp.get("response_metadata") or {}).get("next_cursor") if not cursor: break logger.info("[%s] User sync complete: %d synced, %d skipped across %d page(s)", team_name, total_synced, total_skipped, page)