Skip to content

Commit

Permalink
Update scripts for new API
Browse files Browse the repository at this point in the history
  • Loading branch information
Grazfather committed Mar 14, 2021
1 parent bb77de7 commit ea508af
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 50 deletions.
123 changes: 90 additions & 33 deletions purge/deactivate_all_users.py
Original file line number Diff line number Diff line change
@@ -1,82 +1,139 @@
#!/usr/bin/env python3

from slackclient import SlackClient
import requests
import time
import sys

from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
import requests

# Taken here : https://api.slack.com/custom-integrations/legacy-tokens
SLACK_TOKEN = ""
SLACK_TOKEN = "xoxb-..."

# Available in the HTML source code of https://[team].slack.com/admin
WEB_SLACK_TOKEN = ""
WEB_SLACK_TOKEN = "xoxs-..."

# Channel containing the members we want to deactivate
DEST_CHANNEL = "general"
DEST_CHANNEL = "FILL ME IN"

# Team Slack domain
SLACK_DOMAIN = "opentoallctf.slack.com"

# Users we won't ban even if they are in the channel
safe_user_names = {
"7feilee",
"Kroz",
"Diis",
"r00k",
"Ariana",
"Lord_Idiot",
"UnblvR",
"an0n",
"drtychai",
"enio",
"eriner",
"fevral",
"grazfather",
"idr0p",
"kileak",
"mementomori",
"rh0gue",
"sae",
"sferrini",
"uafio",
"vakzz",
"viva",
"waywardsun",
}

def channel_id_by_name(client, name):
""" Fetch channel ID for a given channel name. """
"""Fetch channel ID for a given channel name."""
limit = 1000
cursor = None
while True:
resp = client.conversations_list(types="public_channel", limit=limit, cursor=cursor)
channels = resp["channels"]

output = client.api_call("channels.list")
channels = output['channels']
for channel in channels:
if channel["name"] == name:
return channel["id"]

channel_id = ''
for channel in channels:
if channel['name'] == name:
return channel['id']
cursor = resp["response_metadata"]["next_cursor"]
if not cursor:
break

return None


def get_all_users(client):
""" Fetch all users in the team. Includes deleted/deactivated users. """
"""Fetch all users in the team. Includes deleted/deactivated users."""
resp = client.users_list()
return resp["members"]

output = client.api_call("users.list")
return output['members']

sc = SlackClient(SLACK_TOKEN)
def get_all_users_in_channel(client, channel_id):
limit = 1000
cursor = None
members = []
while True:
resp = sc.conversations_members(channel=channel_id, limit=1000, cursor=cursor)
cursor = resp["response_metadata"]["next_cursor"]
members += resp["members"]
if not cursor:
break

return members

sc = WebClient(SLACK_TOKEN)

channel_id = channel_id_by_name(sc, DEST_CHANNEL)

if not channel_id:
print("[!] No channel ID found for channel '{}'.".format(DEST_CHANNEL))
sys.exit(1)

print("[*] Found channel {} ({}).".format(DEST_CHANNEL, channel_id))

# Get all members
members = get_all_users(sc)
members = dict([(member['id'], member) for member in members])
members = {member["id"]: member for member in members}
print("[*] Found {} total members.".format(len(members)))

# Get members in channel
output = sc.api_call("channels.info", channel=channel_id)
members_in_channel = output['channel']['members']
members_in_channel = get_all_users_in_channel(sc, channel_id)
print("[*] Found {} members in {}".format(len(members_in_channel), DEST_CHANNEL))

# Get member ids of the safe list
safe_member_ids = {m_id for m_id, member in members.items() if member["name"] in safe_user_names}
print("[*] Found {} blessed users".format(len(safe_member_ids)))

# Filter out bots and deactivated users.
members_to_deactivate = []
for member_id in members_in_channel:
is_deactivated = members[member_id]['deleted']
is_bot = members[member_id]['is_bot']
doomed_members = []

doomed_members = [m_id for m_id in members_in_channel if
not members[m_id]["deleted"] and not members[m_id]["is_bot"]
and not m_id in safe_member_ids]

if not is_deactivated and not is_bot:
members_to_deactivate.append(member_id)
print("[*] Found {} doomed members".format(len(doomed_members)))

# Deactivate members.
# Member deactivation through the slack API is only available for premium teams.
# We can bypass this restriction by using a different API endpoint.
# The code below simulates an admin manually deactivating users through the
# ... web interface.
print("[*] Deactivating {} members.".format(len(members_to_deactivate)))
print("[*] Deactivating {} members.".format(len(doomed_members)))
deactivate_url = "https://{}/api/users.admin.setInactive".format(SLACK_DOMAIN)
for member_id in members_to_deactivate:
for member_id in doomed_members:

username = members[member_id]['profile']['display_name']
data = { "user" : member_id, "token": WEB_SLACK_TOKEN }
headers = { "Content-Type" : "application/x-www-form-urlencoded" }
username = members[member_id]["profile"]["display_name"]
data = {"user": member_id, "token": WEB_SLACK_TOKEN}
headers = {"Content-Type" : "application/x-www-form-urlencoded"}
response = requests.post(deactivate_url, data=data, headers=headers)

print("[*] Kicking {} : {}".format(repr(username), member_id))
print("[*] Banning {} ({})".format(username, member_id))
print(response.text)
if response.json().get("error") == "ratelimited":
time.sleep(1)

# Prevent Slack's rate limiting
time.sleep(1)
# Avoid Slack's rate limiting
time.sleep(0.5)
59 changes: 42 additions & 17 deletions purge/invite_all_users.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,74 @@
#!/usr/bin/env python3

from slackclient import SlackClient
import time
import sys

from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

# Slack API Token
SLACK_TOKEN = ""
SLACK_TOKEN = "xoxb-..."

# Channel to invite users too
DEST_CHANNEL = "general"
DEST_CHANNEL = "FILL ME IN"

def channel_id_by_name(client, name):
""" Fetch channel ID for a given channel name. """
"""Fetch channel ID for a given channel name."""
limit = 1000
cursor = None
while True:
resp = client.conversations_list(types="public_channel", limit=limit, cursor=cursor)
channels = resp["channels"]

output = client.api_call("channels.list")
channels = output['channels']
for channel in channels:
if channel["name"] == name:
return channel["id"]

channel_id = ''
for channel in channels:
if channel['name'] == name:
return channel['id']
cursor = resp["response_metadata"]["next_cursor"]
if not cursor:
break

return None

def get_all_users(client):
""" Fetch all users in the team. Includes deleted/deactivated users. """

output = client.api_call("users.list")
return output['members']
def get_all_users(client):
"""Fetch all users in the team. Includes deleted/deactivated users."""
resp = client.users_list()
return resp["members"]

sc = SlackClient(SLACK_TOKEN)
sc = WebClient(SLACK_TOKEN)

channel_id = channel_id_by_name(sc, DEST_CHANNEL)

if not channel_id:
print("[!] No channel ID found for channel '{}'.".format(DEST_CHANNEL))
sys.exit(1)

print("[*] Found channel {} ({}).".format(DEST_CHANNEL, channel_id))

members = get_all_users(sc)
print("[*] Found {} members.".format(len(members)))

# Join the channel, so we can invite to it
sc.conversations_join(channel=channel_id)

# Invite to channel in groups of 30
# Slack limits channel invitations to 30 members per API call.
print("[*] Inviting users.")
member_ids = [member['id'] for member in members]
member_ids = [member["id"] for member in members]
groups = [member_ids[n:n+30] for n in range(0, len(member_ids), 30)]

for group in groups:
sc.api_call("conversations.invite", channel=channel_id, users=','.join(group))
try:
sc.conversations_invite(channel=channel_id, users=",".join(group))
except SlackApiError as e:
# Technically we can have up to 30 errors, and some might be bad
# e.response.data["errors"] to check them individually
if "ratelimited" in str(e):
time.sleep(1)
elif "cant_invite_self" in str(e):
continue
elif "already_in_channel" in str(e):
continue
# TODO: Should we bail otherwise?
continue

0 comments on commit ea508af

Please sign in to comment.