diff --git a/pyslackersweb/contexts.py b/pyslackersweb/contexts.py index 83bf8ea3..6bfd6539 100644 --- a/pyslackersweb/contexts.py +++ b/pyslackersweb/contexts.py @@ -12,14 +12,18 @@ async def background_jobs(app: web.Application) -> None: github_job = tasks.sync_github_repositories(app) scheduler.add_job(github_job, "interval", hours=1) - slack_job = tasks.sync_slack_timezones(app) - scheduler.add_job(slack_job, "interval", hours=1) + slack_users_job = tasks.sync_slack_users(app) + scheduler.add_job(slack_users_job, "interval", hours=1) + + slack_channels_job = tasks.sync_slack_channels(app) + scheduler.add_job(slack_channels_job, "interval", hours=1) scheduler.start() loop = asyncio.get_running_loop() loop.create_task(github_job()) - loop.create_task(slack_job()) + loop.create_task(slack_users_job()) + loop.create_task(slack_channels_job()) yield diff --git a/pyslackersweb/tasks.py b/pyslackersweb/tasks.py index 45dc6f9e..fddd4255 100644 --- a/pyslackersweb/tasks.py +++ b/pyslackersweb/tasks.py @@ -20,6 +20,15 @@ class Repository: topics: List[str] +@dataclasses.dataclass(frozen=True) # pylint: disable=too-few-public-methods +class Channel: + id: str + name: str + topic: str + purpose: str + members: int + + def sync_github_repositories(app: web.Application): session = app["client_session"] @@ -59,15 +68,15 @@ async def _sync_github() -> None: return _sync_github -def sync_slack_timezones(app: web.Application): +def sync_slack_users(app: web.Application): session = app["client_session"] - async def _sync_slack(): - logger.debug("Refreshing slack user cache.") + async def _sync_slack_users(): + logger.debug("Refreshing slack users cache.") oauth_token = app["slack_token"] if oauth_token is None: - logger.error("No slack oauth token set, unable to sync slack timezones.") + logger.error("No slack oauth token set, unable to sync slack users.") return try: @@ -81,6 +90,10 @@ async def _sync_slack(): ) as r: result = await r.json() + if not result["ok"]: + logger.error("Error fetching users from slack", extra=result) + return + for user in result["members"]: if user["deleted"] or user["is_bot"] or not user["tz"]: continue @@ -103,8 +116,62 @@ async def _sync_slack(): slack_timezones=dict(counter.most_common(100)), slack_user_count=sum(counter.values()), ) - except Exception: + except Exception: # pylint: disable=broad-except logger.exception("Error refreshing slack user cache") - raise + return + + return _sync_slack_users + + +def sync_slack_channels(app: web.Application): + session = app["client_session"] + + async def _sync_slack_channel(): + logger.debug("Refreshing slack channels cache.") + oauth_token = app["slack_token"] + + if oauth_token is None: + logger.error("No slack oauth token set, unable to sync slack channels.") + return + + try: + channels = [] + while True: + params = {} + async with session.get( + "https://slack.com/api/channels.list", + headers={"Authorization": f"Bearer {oauth_token}"}, + params=params, + ) as r: + result = await r.json() + + if not result["ok"]: + logger.error("Error fetching channels from slack", extra=result) + return + + for channel in result["channels"]: + channels.append( + Channel( + id=channel["id"], + name=channel["name"], + topic=channel["topic"]["value"], + purpose=channel["purpose"]["value"], + members=channel["num_members"], + ) + ) + + # next_cursor can be an empty string. We need to check if the value is truthy + if result.get("response_metadata", {}).get("next_cursor"): + params["cursor"] = result["response_metadata"]["next_cursor"] + else: + break + + logger.debug("Found %s slack channels", len(channels)) + + app.update(slack_channels=sorted(channels, key=lambda c: c.name)) + + except Exception: # pylint: disable=broad-except + logger.exception("Error refreshing slack channels cache") + return - return _sync_slack + return _sync_slack_channel