diff options
author | Trygve Aaberge <trygveaa@gmail.com> | 2023-09-30 23:32:09 +0200 |
---|---|---|
committer | Trygve Aaberge <trygveaa@gmail.com> | 2024-02-18 11:32:54 +0100 |
commit | 2733d6a0faf3ea9150ee8e16950ca3d54a6361e6 (patch) | |
tree | 1807e7b6b5c191607e032223e15a4dad87ebd582 /slack | |
parent | 9c121f76db9285ffc06388bd899c88ce01a2c73a (diff) | |
download | wee-slack-2733d6a0faf3ea9150ee8e16950ca3d54a6361e6.tar.gz |
Split users.info into multiple requests when necessary
If you try to request several thousand users in one request (not sure
exactly what the limit is), it just returns HTTP 500, so split it into
multiple requests with a max of 1000 users per request.
Diffstat (limited to 'slack')
-rw-r--r-- | slack/slack_api.py | 16 | ||||
-rw-r--r-- | slack/util.py | 42 |
2 files changed, 56 insertions, 2 deletions
diff --git a/slack/slack_api.py b/slack/slack_api.py index 0e38bf4..3aa1d57 100644 --- a/slack/slack_api.py +++ b/slack/slack_api.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +from itertools import chain from typing import TYPE_CHECKING, Iterable, Mapping, Optional, Sequence, Union from urllib.parse import urlencode @@ -8,6 +9,8 @@ from slack.error import SlackApiError from slack.http import http_request from slack.shared import shared from slack.slack_message import SlackTs +from slack.task import gather +from slack.util import chunked if TYPE_CHECKING: from slack_api.slack_bots_info import SlackBotInfoResponse, SlackBotsInfoResponse @@ -204,7 +207,7 @@ class SlackApi(SlackApiCommon): raise SlackApiError(self.workspace, method, response, params) return response - async def fetch_users_info(self, user_ids: Iterable[str]): + async def _fetch_users_info_without_splitting(self, user_ids: Iterable[str]): method = "users.info" params: Params = {"users": ",".join(user_ids)} response: SlackUsersInfoResponse = await self._fetch(method, params) @@ -212,6 +215,17 @@ class SlackApi(SlackApiCommon): raise SlackApiError(self.workspace, method, response, params) return response + async def fetch_users_info(self, user_ids: Iterable[str]): + responses = await gather( + *( + self._fetch_users_info_without_splitting(user_ids_batch) + for user_ids_batch in chunked(user_ids, 1000) + ) + ) + users = list(chain(*(response["users"] for response in responses))) + response: SlackUsersInfoResponse = {"ok": True, "users": users} + return response + async def fetch_bot_info(self, bot_id: str): method = "bots.info" params: Params = {"bot": bot_id} diff --git a/slack/util.py b/slack/util.py index 647ee02..5c45d9d 100644 --- a/slack/util.py +++ b/slack/util.py @@ -1,11 +1,15 @@ from __future__ import annotations -from typing import Callable, Optional +from functools import partial +from itertools import islice +from typing import Callable, Iterable, Iterator, List, Optional, TypeVar import weechat from slack.shared import WeechatCallbackReturnType, shared +T = TypeVar("T") + def get_callback_name(callback: Callable[..., WeechatCallbackReturnType]) -> str: callback_id = f"{callback.__name__}-{id(callback)}" @@ -18,3 +22,39 @@ def with_color(color: Optional[str], string: str, reset_color: str = "reset"): return f"{weechat.color(color)}{string}{weechat.color(reset_color)}" else: return string + + +# From https://github.com/more-itertools/more-itertools/blob/v10.1.0/more_itertools/recipes.py#L93-L106 +def take(n: int, iterable: Iterable[T]) -> List[T]: + """Return first *n* items of the iterable as a list. + + >>> take(3, range(10)) + [0, 1, 2] + + If there are fewer than *n* items in the iterable, all of them are + returned. + + >>> take(10, range(3)) + [0, 1, 2] + + """ + return list(islice(iterable, n)) + + +# Modified from https://github.com/more-itertools/more-itertools/blob/v10.1.0/more_itertools/more.py#L149-L181 +def chunked(iterable: Iterable[T], n: int, strict: bool = False) -> Iterator[List[T]]: + """Break *iterable* into lists of length *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) + [[1, 2, 3], [4, 5, 6]] + + The last yielded list will have fewer than *n* elements + if the length of *iterable* is not divisible by *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) + [[1, 2, 3], [4, 5, 6], [7, 8]] + + To use a fill-in value instead, see the :func:`grouper` recipe. + + """ + return iter(partial(take, n, iter(iterable)), []) |