aboutsummaryrefslogtreecommitdiffstats
path: root/slack
diff options
context:
space:
mode:
authorTrygve Aaberge <trygveaa@gmail.com>2023-09-30 23:32:09 +0200
committerTrygve Aaberge <trygveaa@gmail.com>2024-02-18 11:32:54 +0100
commit2733d6a0faf3ea9150ee8e16950ca3d54a6361e6 (patch)
tree1807e7b6b5c191607e032223e15a4dad87ebd582 /slack
parent9c121f76db9285ffc06388bd899c88ce01a2c73a (diff)
downloadwee-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.py16
-rw-r--r--slack/util.py42
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)), [])