-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathslack_client.py
238 lines (182 loc) · 8.99 KB
/
slack_client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
import os
import requests
import logging
import time
from datetime import datetime
from utils import validate_token
# Set up logging
logger = logging.getLogger(__name__)
class SlackClient:
"""
Slack API client.
"""
def __init__(self, config):
self.config = config
self.token = os.getenv("SLACK_API_TOKEN")
validate_token("SLACK_API_TOKEN", self.token)
self.workspace_id = config["slack"]["workspace_id"]
self.headers = {
"Authorization": f"Bearer {self.token}"
# Removed Content-Type header as it might be causing issues with some API endpoints
}
logger.info("Initialized Slack client")
def get_member_count(self):
"""
Get the number of members in the Slack workspace.
Returns:
Dictionary with date and member count
"""
url = "https://slack.com/api/users.list"
params = {"limit": 1000} # Maximum allowed by Slack API
logger.info("Fetching Slack members")
# Make initial request
response = requests.get(url, headers=self.headers, params=params)
if not response.ok:
raise Exception(f"Failed to fetch Slack members: {response.text}")
data = response.json()
if not data.get("ok"):
raise Exception(f"Slack API error: {data.get('error')}")
# Initialize members list
all_members = data.get("members", [])
# Handle pagination if there are more members
while data.get("response_metadata", {}).get("next_cursor"):
next_cursor = data["response_metadata"]["next_cursor"]
if not next_cursor:
break
# Add cursor to params for next page
params["cursor"] = next_cursor
# Add a small delay to avoid rate limiting
time.sleep(0.5)
# Fetch next page
response = requests.get(url, headers=self.headers, params=params)
if not response.ok:
logger.warning(f"Failed to fetch next page of members: {response.text}")
break
data = response.json()
if not data.get("ok"):
logger.warning(f"Slack API error: {data.get('error')}")
break
all_members.extend(data.get("members", []))
# Count active members (not bots or deleted users)
active_members = [m for m in all_members if not m.get("is_bot") and not m.get("deleted")]
member_count = len(active_members)
logger.info(f"Found {member_count} active members in Slack workspace")
return {
"date": datetime.now().strftime("%Y-%m-%d"),
"member_count": member_count
}
def get_channel_stats(self, channel_id=None):
"""
Get statistics for a specific channel or all channels.
Args:
channel_id: Optional channel ID to get stats for
Returns:
List of dictionaries with channel statistics
"""
# Get list of channels (public channels only)
channels_url = "https://slack.com/api/conversations.list"
logger.info("Fetching public Slack channels")
if channel_id:
response = requests.get(f"{channels_url}?channel={channel_id}&types=public_channel", headers=self.headers)
else:
response = requests.get(f"{channels_url}?types=public_channel", headers=self.headers)
if not response.ok:
raise Exception(f"Failed to fetch Slack channels: {response.text}")
data = response.json()
if not data.get("ok"):
raise Exception(f"Slack API error: {data.get('error')}")
channels = data.get("channels", [])
# Get statistics for each channel
channel_stats = []
for channel in channels:
channel_id = channel.get("id")
channel_name = channel.get("name")
# Get channel info
info_url = f"https://slack.com/api/conversations.info"
info_params = {"channel": channel_id}
info_response = requests.get(info_url, headers=self.headers, params=info_params)
if not info_response.ok:
logger.warning(f"Failed to fetch info for channel {channel_name}: {info_response.text}")
continue
info_data = info_response.json()
if not info_data.get("ok"):
logger.warning(f"Slack API error for channel {channel_name}: {info_data.get('error')}")
continue
channel_info = info_data.get("channel", {})
# Get member count
members_url = f"https://slack.com/api/conversations.members"
members_params = {"channel": channel_id}
members_response = requests.get(members_url, headers=self.headers, params=members_params)
member_count = 0
if members_response.ok:
members_data = members_response.json()
if members_data.get("ok"):
member_count = len(members_data.get("members", []))
# Compile statistics
channel_stats.append({
"date": datetime.now().strftime("%Y-%m-%d"),
"channel_id": channel_id,
"channel_name": channel_name,
"member_count": member_count,
"is_private": channel_info.get("is_private", False),
"created": channel_info.get("created"),
"topic": channel_info.get("topic", {}).get("value", ""),
"purpose": channel_info.get("purpose", {}).get("value", "")
})
logger.info(f"Retrieved statistics for {len(channel_stats)} Slack channels")
return channel_stats
# The get_active_channels method has been removed because it required the channels:history scope
# and would have required adding the bot to all channels
def get_workspace_activity(self):
"""
Get overall workspace statistics (without message activity).
Required Slack scopes:
- channels:read (for channel list)
- users:read (for member count)
Returns:
Dictionary with workspace statistics
"""
# Get total channels (public channels only)
channels_url = "https://slack.com/api/conversations.list"
params = {"types": "public_channel", "limit": 1000} # Only request public channels, with pagination
logger.info("Fetching total public Slack channels")
total_channel_count = 0
# Make initial request
response = requests.get(channels_url, headers=self.headers, params=params)
if response.ok:
data = response.json()
if data.get("ok"):
channels = data.get("channels", [])
total_channel_count += len(channels)
# Handle pagination if there are more channels
while data.get("response_metadata", {}).get("next_cursor"):
next_cursor = data["response_metadata"]["next_cursor"]
if not next_cursor:
break
# Add cursor to params for next page
params["cursor"] = next_cursor
# Add a small delay to avoid rate limiting
time.sleep(0.5)
# Fetch next page
response = requests.get(channels_url, headers=self.headers, params=params)
if not response.ok:
logger.warning(f"Failed to fetch next page of channels: {response.text}")
break
data = response.json()
if not data.get("ok"):
logger.warning(f"Slack API error: {data.get('error')}")
break
channels = data.get("channels", [])
total_channel_count += len(channels)
logger.info(f"Found {total_channel_count} public channels in total")
# Get member count
member_data = self.get_member_count()
member_count = member_data.get("member_count", 0)
workspace_activity = {
"timestamp": datetime.now().isoformat(),
"date": datetime.now().strftime("%Y-%m-%d"),
"total_channels": total_channel_count,
"member_count": member_count
}
logger.info(f"Retrieved workspace statistics: {total_channel_count} channels, {member_count} members")
return workspace_activity