slack-objects 0.0.post31__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- slack_objects/__init__.py +22 -0
- slack_objects/_version.py +34 -0
- slack_objects/api_caller.py +42 -0
- slack_objects/base.py +30 -0
- slack_objects/client.py +50 -0
- slack_objects/config.py +29 -0
- slack_objects/conversations.py +437 -0
- slack_objects/files.py +331 -0
- slack_objects/idp_groups.py +200 -0
- slack_objects/messages.py +322 -0
- slack_objects/rate_limits.py +51 -0
- slack_objects/users.py +554 -0
- slack_objects/workspaces.py +261 -0
- slack_objects-0.0.post31.dist-info/METADATA +201 -0
- slack_objects-0.0.post31.dist-info/RECORD +18 -0
- slack_objects-0.0.post31.dist-info/WHEEL +5 -0
- slack_objects-0.0.post31.dist-info/licenses/LICENSE +21 -0
- slack_objects-0.0.post31.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
slack_objects.workspaces
|
|
5
|
+
=======================
|
|
6
|
+
|
|
7
|
+
Workspaces helper for the `slack-objects` package.
|
|
8
|
+
|
|
9
|
+
Merged/refactored from two legacy implementations:
|
|
10
|
+
- A "single-workspace" helper that fetches attributes via `team.info` (PCbot) :contentReference[oaicite:2]{index=2}
|
|
11
|
+
- A "grid admin" helper that lists workspaces and can list workspace users/admins via admin endpoints :contentReference[oaicite:3]{index=3}
|
|
12
|
+
|
|
13
|
+
Design goals:
|
|
14
|
+
- Factory-friendly: `workspaces = slack.workspaces()` or `ws = slack.workspaces("T123")`
|
|
15
|
+
- Modularity: public methods call wrapper methods; wrappers are the only place that directly call Slack API
|
|
16
|
+
- Usability: supports both "work with one workspace" and "work with many workspaces in a grid"
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from typing import Any, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
from .base import SlackObjectBase
|
|
23
|
+
from .config import RateTier
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Workspaces(SlackObjectBase):
|
|
28
|
+
"""
|
|
29
|
+
Workspaces domain helper.
|
|
30
|
+
|
|
31
|
+
Factory-style usage:
|
|
32
|
+
slack = SlackObjectsClient(cfg)
|
|
33
|
+
workspaces = slack.workspaces() # unbound (grid/listing helpers)
|
|
34
|
+
ws = slack.workspaces("T12345678") # bound to workspace_id
|
|
35
|
+
|
|
36
|
+
Notes:
|
|
37
|
+
- `workspace_id` is optional. Methods that require a workspace will enforce it.
|
|
38
|
+
- `workspaces_cache` is an optional cached list of workspaces (from admin.teams.list).
|
|
39
|
+
This enables fast name<->id lookups without re-fetching each time.
|
|
40
|
+
"""
|
|
41
|
+
workspace_id: Optional[str] = None
|
|
42
|
+
attributes: Dict[str, Any] = field(default_factory=dict)
|
|
43
|
+
|
|
44
|
+
# Cache of workspaces returned by admin.teams.list (list of dicts with at least id/name)
|
|
45
|
+
workspaces_cache: List[Dict[str, Any]] = field(default_factory=list)
|
|
46
|
+
|
|
47
|
+
# ---------- factory helpers ----------
|
|
48
|
+
|
|
49
|
+
def with_workspace(self, workspace_id: str) -> "Workspaces":
|
|
50
|
+
"""Return a new Workspaces instance bound to workspace_id, sharing cfg/client/logger/api."""
|
|
51
|
+
return Workspaces(
|
|
52
|
+
cfg=self.cfg,
|
|
53
|
+
client=self.client,
|
|
54
|
+
logger=self.logger,
|
|
55
|
+
api=self.api,
|
|
56
|
+
workspace_id=workspace_id,
|
|
57
|
+
workspaces_cache=self.workspaces_cache,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# ---------- attribute lifecycle ----------
|
|
61
|
+
|
|
62
|
+
def refresh(self, workspace_id: Optional[str] = None) -> Dict[str, Any]:
|
|
63
|
+
"""
|
|
64
|
+
Refresh attributes for workspace_id (or self.workspace_id) using team.info.
|
|
65
|
+
|
|
66
|
+
This method is intentionally layered: it calls `get_workspace_info()`.
|
|
67
|
+
"""
|
|
68
|
+
if workspace_id:
|
|
69
|
+
self.workspace_id = workspace_id
|
|
70
|
+
if not self.workspace_id:
|
|
71
|
+
raise ValueError("refresh() requires workspace_id (passed or already set)")
|
|
72
|
+
|
|
73
|
+
resp = self.get_workspace_info(self.workspace_id)
|
|
74
|
+
if not resp.get("ok"):
|
|
75
|
+
raise RuntimeError(f"Workspaces.get_workspace_info() failed: {resp}")
|
|
76
|
+
|
|
77
|
+
# `team.info` returns `team` on success in the legacy version :contentReference[oaicite:4]{index=4}
|
|
78
|
+
self.attributes = resp.get("team") or {}
|
|
79
|
+
return self.attributes
|
|
80
|
+
|
|
81
|
+
def _require_workspace_id(self, workspace_id: Optional[str] = None) -> str:
|
|
82
|
+
"""Return a workspace_id or raise, used by methods that require one."""
|
|
83
|
+
wid = workspace_id or self.workspace_id
|
|
84
|
+
if not wid:
|
|
85
|
+
raise ValueError("This operation requires a workspace_id (passed or bound).")
|
|
86
|
+
return wid
|
|
87
|
+
|
|
88
|
+
# ============================================================
|
|
89
|
+
# Slack API wrapper layer
|
|
90
|
+
# ============================================================
|
|
91
|
+
# Only these methods should call `self.api.call(...)` directly.
|
|
92
|
+
|
|
93
|
+
def _team_info(self, workspace_id: str) -> Dict[str, Any]:
|
|
94
|
+
"""Wrapper for team.info (fetch a workspace's metadata)."""
|
|
95
|
+
return self.api.call(self.client, "team.info", rate_tier=RateTier.TIER_3, team=workspace_id)
|
|
96
|
+
|
|
97
|
+
def _admin_teams_list(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
98
|
+
"""Wrapper for admin.teams.list (Grid: list workspaces)."""
|
|
99
|
+
return self.api.call(self.client, "admin.teams.list", rate_tier=RateTier.TIER_3, **payload)
|
|
100
|
+
|
|
101
|
+
def _admin_users_list(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
102
|
+
"""Wrapper for admin.users.list (list users in a workspace)."""
|
|
103
|
+
return self.api.call(self.client, "admin.users.list", rate_tier=RateTier.TIER_4, **payload)
|
|
104
|
+
|
|
105
|
+
def _admin_teams_admins_list(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
106
|
+
"""Wrapper for admin.teams.admins.list (list admin IDs for a workspace)."""
|
|
107
|
+
return self.api.call(self.client, "admin.teams.admins.list", rate_tier=RateTier.TIER_3, **payload)
|
|
108
|
+
|
|
109
|
+
# ============================================================
|
|
110
|
+
# Public API (calls wrappers above)
|
|
111
|
+
# ============================================================
|
|
112
|
+
|
|
113
|
+
def get_workspace_info(self, workspace_id: str) -> Dict[str, Any]:
|
|
114
|
+
"""Public method for team.info."""
|
|
115
|
+
return self._team_info(workspace_id)
|
|
116
|
+
|
|
117
|
+
def list_workspaces(self, *, force_refresh: bool = False) -> List[Dict[str, Any]]:
|
|
118
|
+
"""
|
|
119
|
+
Return a list of workspaces in the Enterprise Grid (admin.teams.list), paginated.
|
|
120
|
+
|
|
121
|
+
This replaces the legacy constructor-side fetching of all workspaces :contentReference[oaicite:5]{index=5}.
|
|
122
|
+
Results are cached in `workspaces_cache` unless `force_refresh=True`.
|
|
123
|
+
"""
|
|
124
|
+
if self.workspaces_cache and not force_refresh:
|
|
125
|
+
return self.workspaces_cache
|
|
126
|
+
|
|
127
|
+
workspaces: List[Dict[str, Any]] = []
|
|
128
|
+
payload: Dict[str, Any] = {}
|
|
129
|
+
|
|
130
|
+
while True:
|
|
131
|
+
resp = self._admin_teams_list(payload)
|
|
132
|
+
if not resp.get("ok"):
|
|
133
|
+
raise RuntimeError(f"admin.teams.list failed: {resp}")
|
|
134
|
+
|
|
135
|
+
teams = resp.get("teams") or []
|
|
136
|
+
workspaces.extend(teams)
|
|
137
|
+
|
|
138
|
+
# Slack commonly returns cursor pagination via response_metadata.next_cursor
|
|
139
|
+
meta = resp.get("response_metadata") or {}
|
|
140
|
+
cursor = meta.get("next_cursor") or ""
|
|
141
|
+
if cursor:
|
|
142
|
+
payload["cursor"] = cursor
|
|
143
|
+
else:
|
|
144
|
+
break
|
|
145
|
+
|
|
146
|
+
self.workspaces_cache = workspaces
|
|
147
|
+
return workspaces
|
|
148
|
+
|
|
149
|
+
# ----- name/id resolution helpers (from legacy SlackAdmin) -----
|
|
150
|
+
|
|
151
|
+
def get_workspace_name(self, workspace_id: str, *, force_refresh: bool = False) -> str:
|
|
152
|
+
"""
|
|
153
|
+
Resolve a workspace ID -> workspace name using the cached list from admin.teams.list.
|
|
154
|
+
|
|
155
|
+
Legacy behavior raised if not found :contentReference[oaicite:6]{index=6}.
|
|
156
|
+
"""
|
|
157
|
+
workspaces = self.list_workspaces(force_refresh=force_refresh)
|
|
158
|
+
for ws in workspaces:
|
|
159
|
+
if ws.get("id") == workspace_id:
|
|
160
|
+
name = ws.get("name")
|
|
161
|
+
if name:
|
|
162
|
+
return str(name)
|
|
163
|
+
|
|
164
|
+
raise ValueError(
|
|
165
|
+
f"Could not find a workspace with id '{workspace_id}'. "
|
|
166
|
+
"Check the id/token scopes and ensure you are targeting the correct Grid."
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def get_workspace_id(self, workspace_name: str, *, force_refresh: bool = False) -> str:
|
|
170
|
+
"""
|
|
171
|
+
Resolve a workspace name -> workspace ID using the cached list from admin.teams.list.
|
|
172
|
+
|
|
173
|
+
Legacy behavior raised if not found :contentReference[oaicite:7]{index=7}.
|
|
174
|
+
"""
|
|
175
|
+
workspaces = self.list_workspaces(force_refresh=force_refresh)
|
|
176
|
+
target = workspace_name.strip().lower()
|
|
177
|
+
|
|
178
|
+
for ws in workspaces:
|
|
179
|
+
if str(ws.get("name", "")).strip().lower() == target:
|
|
180
|
+
wid = ws.get("id")
|
|
181
|
+
if wid:
|
|
182
|
+
return str(wid)
|
|
183
|
+
|
|
184
|
+
raise ValueError(
|
|
185
|
+
f"Could not find a workspace with name '{workspace_name}'. "
|
|
186
|
+
"Check the name/token scopes and ensure you are targeting the correct Grid."
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def get_workspace_from_name(self, workspace_name: str, *, force_refresh: bool = False) -> Dict[str, Any]:
|
|
190
|
+
"""
|
|
191
|
+
Return the workspace dict that matches the provided name.
|
|
192
|
+
|
|
193
|
+
Legacy behavior raised if not found :contentReference[oaicite:8]{index=8}.
|
|
194
|
+
"""
|
|
195
|
+
workspaces = self.list_workspaces(force_refresh=force_refresh)
|
|
196
|
+
target = workspace_name.strip().lower()
|
|
197
|
+
|
|
198
|
+
for ws in workspaces:
|
|
199
|
+
if str(ws.get("name", "")).strip().lower() == target:
|
|
200
|
+
return ws
|
|
201
|
+
|
|
202
|
+
raise ValueError(
|
|
203
|
+
f"Could not find a workspace with name '{workspace_name}'. "
|
|
204
|
+
"Check the name/token scopes and ensure you are targeting the correct Grid."
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# ----- workspace membership helpers (from legacy SlackAdmin) -----
|
|
208
|
+
|
|
209
|
+
def list_users(self, workspace_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
210
|
+
"""
|
|
211
|
+
Return a list of users in a workspace via admin.users.list (paginated).
|
|
212
|
+
|
|
213
|
+
This matches the legacy behavior of returning `data['users']` across pages :contentReference[oaicite:9]{index=9}.
|
|
214
|
+
"""
|
|
215
|
+
wid = self._require_workspace_id(workspace_id)
|
|
216
|
+
|
|
217
|
+
payload: Dict[str, Any] = {"team_id": wid}
|
|
218
|
+
users: List[Dict[str, Any]] = []
|
|
219
|
+
|
|
220
|
+
while True:
|
|
221
|
+
resp = self._admin_users_list(payload)
|
|
222
|
+
if not resp.get("ok"):
|
|
223
|
+
raise RuntimeError(f"admin.users.list failed: {resp}")
|
|
224
|
+
|
|
225
|
+
users.extend(resp.get("users") or [])
|
|
226
|
+
|
|
227
|
+
meta = resp.get("response_metadata") or {}
|
|
228
|
+
cursor = meta.get("next_cursor") or ""
|
|
229
|
+
if cursor:
|
|
230
|
+
payload["cursor"] = cursor
|
|
231
|
+
else:
|
|
232
|
+
break
|
|
233
|
+
|
|
234
|
+
return users
|
|
235
|
+
|
|
236
|
+
def list_admin_ids(self, workspace_id: Optional[str] = None) -> List[str]:
|
|
237
|
+
"""
|
|
238
|
+
Return a list of admin user IDs for a workspace via admin.teams.admins.list (paginated).
|
|
239
|
+
|
|
240
|
+
Legacy version returned list_of_admins (IDs) :contentReference[oaicite:10]{index=10}.
|
|
241
|
+
"""
|
|
242
|
+
wid = self._require_workspace_id(workspace_id)
|
|
243
|
+
|
|
244
|
+
payload: Dict[str, Any] = {"team_id": wid}
|
|
245
|
+
admin_ids: List[str] = []
|
|
246
|
+
|
|
247
|
+
while True:
|
|
248
|
+
resp = self._admin_teams_admins_list(payload)
|
|
249
|
+
if not resp.get("ok"):
|
|
250
|
+
raise RuntimeError(f"admin.teams.admins.list failed: {resp}")
|
|
251
|
+
|
|
252
|
+
admin_ids.extend([str(x) for x in (resp.get("admin_ids") or [])])
|
|
253
|
+
|
|
254
|
+
meta = resp.get("response_metadata") or {}
|
|
255
|
+
cursor = meta.get("next_cursor") or ""
|
|
256
|
+
if cursor:
|
|
257
|
+
payload["cursor"] = cursor
|
|
258
|
+
else:
|
|
259
|
+
break
|
|
260
|
+
|
|
261
|
+
return admin_ids
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: slack-objects
|
|
3
|
+
Version: 0.0.post31
|
|
4
|
+
Summary: This package defines classes for working with slack objects like users, conversations, messages, etc.
|
|
5
|
+
Author-email: "Marcos E. Mercado" <marcos_elias@hotmail.com>
|
|
6
|
+
Keywords: slack,objects,classes,slack objects,utilities,slack utilities,slack object types,slack types,types
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: slack-sdk
|
|
14
|
+
Requires-Dist: PC_Utils
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# slack-objects
|
|
18
|
+
|
|
19
|
+
A focused Python package for working with **Slack objects** commonly used in administration and automation workflows.
|
|
20
|
+
|
|
21
|
+
The following Slack object types will be supported:
|
|
22
|
+
|
|
23
|
+
- **Users**
|
|
24
|
+
- **Conversations**
|
|
25
|
+
- **Messages**
|
|
26
|
+
- **Files**
|
|
27
|
+
- **Workspaces**
|
|
28
|
+
- **IDP_groups**
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Overview
|
|
34
|
+
|
|
35
|
+
`slack-objects` provides lightweight, reusable classes that wrap Slack Web API, Admin API, and SCIM operations in a consistent, object-oriented way. It is designed for:
|
|
36
|
+
|
|
37
|
+
- Slack administration automation
|
|
38
|
+
- Identity and access management flows
|
|
39
|
+
- Internal tooling and bots
|
|
40
|
+
- Auditing and cleanup scripts
|
|
41
|
+
|
|
42
|
+
The package does **not** aim to be a full Slack SDK replacement. Instead, it focuses on common higher-level tasks that typically require multiple API calls and boilerplate logic.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Requirements
|
|
47
|
+
|
|
48
|
+
- Python **3.9+**
|
|
49
|
+
- Slack app with appropriate scopes
|
|
50
|
+
- Tokens provided via environment variables or from Azure KeyVault using PC_Azure package (`python -m pip install PC_Azure`)
|
|
51
|
+
|
|
52
|
+
Typical dependencies:
|
|
53
|
+
- `slack_sdk`
|
|
54
|
+
- `requests`
|
|
55
|
+
- `python-dotenv` (optional)
|
|
56
|
+
- `PC_Azure` (optional)
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install -r requirements.txt
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Classes and usage
|
|
67
|
+
|
|
68
|
+
### `Users`
|
|
69
|
+
|
|
70
|
+
Purpose: actions related to Slack users.
|
|
71
|
+
|
|
72
|
+
Constructor:
|
|
73
|
+
```python
|
|
74
|
+
Users(global_vars, client, logger, user_id="")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Key methods:
|
|
78
|
+
- `is_contingent_worker()` → bool using name/display name label `[External]`.
|
|
79
|
+
- `is_guest()` → bool if `is_restricted` or `is_ultra_restricted`.
|
|
80
|
+
- `make_multi_channel_guest(token, scim_version='v1')` → `requests.Response` via SCIM (v1/v2).
|
|
81
|
+
- `remove_from_channels(token, client, logger, channel_ids)` → remove user from channels (admin API).
|
|
82
|
+
- `remove_from_workspaces(client, logger, workspace_ids, keep=[])` → remove user from workspaces.
|
|
83
|
+
- `ap_studio_process()` → composite flow: convert to MCG, remove from org-wide channels, remove from other workspaces.
|
|
84
|
+
- `get_userId_from_email(email)` → Slack user ID or empty string.
|
|
85
|
+
- `is_user_authorized(service_name, auth_level='read')` → bool based on IdP group membership.
|
|
86
|
+
- `invite_user(channel_ids, email, team_id, email_password_policy_enabled=False)` → invite a user, returns response string.
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
```python
|
|
90
|
+
u = Users(global_vars, client, logger, user_id="U123")
|
|
91
|
+
if u.is_contingent_worker():
|
|
92
|
+
u.make_multi_channel_guest(token=global_vars.user_token)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `Conversations`
|
|
96
|
+
|
|
97
|
+
Purpose: actions related to conversations (e.g., channels).
|
|
98
|
+
|
|
99
|
+
Constructor:
|
|
100
|
+
```python
|
|
101
|
+
Conversations(global_vars, client, logger, channel_id)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Key methods:
|
|
105
|
+
- `is_private()` → bool.
|
|
106
|
+
- `get_messages(channel_id="", include_all_metadata=False, limit=None, inclusive=True, latest=None, oldest=None)` → list of messages using `conversations.history` with pagination.
|
|
107
|
+
|
|
108
|
+
Example:
|
|
109
|
+
```python
|
|
110
|
+
ch = Conversations(global_vars, client, logger, channel_id="C123")
|
|
111
|
+
msgs = ch.get_messages(limit=100)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `Messages`
|
|
115
|
+
|
|
116
|
+
Purpose: manage Slack messages and blocks.
|
|
117
|
+
|
|
118
|
+
Constructor:
|
|
119
|
+
```python
|
|
120
|
+
Messages(global_vars, client, logger, channel_id, ts, message=None)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Key methods:
|
|
124
|
+
- `update_message(as_user=True, channel_id="", message_ts="", new_message_blocks=[], new_message_text="", new_message_attachments="")` → update message via `chat.update`.
|
|
125
|
+
- `replace_message_block(blocks=[], block_type="", block_id="", text="", new_block={}, new_block_id="")` → find a block by type or id and replace it, then update message.
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
```python
|
|
129
|
+
msg = Messages(global_vars, client, logger, "C123", "1717000000.000100")
|
|
130
|
+
msg.update_message(new_message_text="Updated content")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### `Files`
|
|
134
|
+
|
|
135
|
+
Purpose: interact with files in Slack.
|
|
136
|
+
|
|
137
|
+
Constructor:
|
|
138
|
+
```python
|
|
139
|
+
Files(global_vars, client, logger, file_id="", get_content=False)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Key methods:
|
|
143
|
+
- `get_text_content()` → fetch content for text files via `url_private` (uses bot token).
|
|
144
|
+
- `upload_to_slack(title, channel="", thread_ts="")` → upload the current file content via `files_upload_v2`.
|
|
145
|
+
- `delete_file(file_id="")` → delete a file by id.
|
|
146
|
+
- `list_files(**args)` → simple wrapper around `files.list`.
|
|
147
|
+
- `get_file_source_message(channel: Channels, file_id="", user_id="")` → find the message where a file was shared (looks back ~5 messages).
|
|
148
|
+
|
|
149
|
+
Example:
|
|
150
|
+
```python
|
|
151
|
+
f = Files(global_vars, client, logger, file_id="F123", get_content=True)
|
|
152
|
+
f.upload_to_slack(title="Processed file", channel="C123")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### `Workspaces`
|
|
156
|
+
|
|
157
|
+
Purpose: workspace info helper.
|
|
158
|
+
|
|
159
|
+
Constructor:
|
|
160
|
+
```python
|
|
161
|
+
Workspaces(client, logger, workspace_id)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Obtains attributes via `team.info`.
|
|
165
|
+
|
|
166
|
+
### `IDP_groups`
|
|
167
|
+
|
|
168
|
+
Purpose: manage IdP (Okta) groups via SCIM.
|
|
169
|
+
|
|
170
|
+
Constructor:
|
|
171
|
+
```python
|
|
172
|
+
IDP_groups(global_vars)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Key methods:
|
|
176
|
+
- `get_groups()` → list of `{ 'group id', 'group name' }` (paginated).
|
|
177
|
+
- `get_members(group_id)` → list of members with `value` (user id) and `display` (name).
|
|
178
|
+
- `is_member(user_id, group_id)` → bool.
|
|
179
|
+
|
|
180
|
+
Example:
|
|
181
|
+
```python
|
|
182
|
+
idp = IDP_groups(global_vars)
|
|
183
|
+
if idp.is_member("U123", "GP456"):
|
|
184
|
+
print("authorized")
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Tokens and rate limits
|
|
188
|
+
|
|
189
|
+
- Methods that call admin or SCIM APIs require the User OAuth token.
|
|
190
|
+
- Standard Web API calls may use the Bot token via `App.client`.
|
|
191
|
+
- Some methods respect internal wait times (e.g., `Tier_2`, `Tier_3`, `Tier_4`) to avoid rate limits. Configure these in `libraries_and_globals.py`.
|
|
192
|
+
|
|
193
|
+
## Error handling
|
|
194
|
+
|
|
195
|
+
- Methods catch `SlackApiError` and log messages via the provided `logger`.
|
|
196
|
+
- Some methods post audit logs to channels configured in `global_vars`.
|
|
197
|
+
|
|
198
|
+
## Notes
|
|
199
|
+
|
|
200
|
+
- SCIM version: production uses `v1`, sandbox may use `v2`.
|
|
201
|
+
- `Files.get_text_content()` is designed for `text/*` mimetypes.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
slack_objects/__init__.py,sha256=wYczk9CK67k2nrq8rtlxVqGMu_93wBNGIf_Ha-GH5mE,525
|
|
2
|
+
slack_objects/_version.py,sha256=SjQ_icTK6fnX32Dc0GVEVLmDoZTtEN7KhTlrIniW_QY,750
|
|
3
|
+
slack_objects/api_caller.py,sha256=Gch_DFpAgQN7Svgoe9Y4F4R2FY4PfiBchXuGbZ0sOgw,1507
|
|
4
|
+
slack_objects/base.py,sha256=mCKi5u1apj1vQTuNwlIcFf-A4Yh_Ag3lTp6kXJVJa3s,1097
|
|
5
|
+
slack_objects/client.py,sha256=K3Ln6SGkgfc9i2IVIvFlcowX8Cgp2E26RnKf7XRr46k,1966
|
|
6
|
+
slack_objects/config.py,sha256=Pe_5SGXNvbZEgKYA6DdsQYpGl1Uod2m41K385QI_ib0,809
|
|
7
|
+
slack_objects/conversations.py,sha256=ySJqUixCKQKkUPd-_6AeFO99uYyNq4qscqpkZ1OzRbs,17837
|
|
8
|
+
slack_objects/files.py,sha256=g6LnP1pWpEKmHeClF6IusABrKY4nSKyZxI0X8NXnEbg,12540
|
|
9
|
+
slack_objects/idp_groups.py,sha256=Jf2Sr6PIO-avgbbt6iJT_g7Qdrzs_Yxz0jd6C8YDTWo,7778
|
|
10
|
+
slack_objects/messages.py,sha256=PsUSqUfX5gtAwl7BKusT1AnTLqKRJgz4bkuXc1W4If4,11978
|
|
11
|
+
slack_objects/rate_limits.py,sha256=cSfG9k04DlpyOQRb8r5IFquKfgbrAEQHX4G9oGd8QcE,1496
|
|
12
|
+
slack_objects/users.py,sha256=BvLYy-Ng82Mv5pnIPS_eF6hYXk-6upeEsMq-9QrTCG8,22315
|
|
13
|
+
slack_objects/workspaces.py,sha256=1yUWIca61fq1OwB6xMJZJFCLh7HNmpku4vhjhvCJg-A,10744
|
|
14
|
+
slack_objects-0.0.post31.dist-info/licenses/LICENSE,sha256=gIj0uwoGs4CwZR0bAP1ZP-F6B5f7VWJDz3Kfpc3c0dI,1090
|
|
15
|
+
slack_objects-0.0.post31.dist-info/METADATA,sha256=gaqROB2GRuwm8FJ6O5GzLOOs7Wddg2HmXg3buGpC0-k,6379
|
|
16
|
+
slack_objects-0.0.post31.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
17
|
+
slack_objects-0.0.post31.dist-info/top_level.txt,sha256=enSaCqZ69Tu_AzK_F0_NEtvRK-r5OjpMHJsnFh5Z8Wo,14
|
|
18
|
+
slack_objects-0.0.post31.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marcos Mercado
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
slack_objects
|