Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update invite script to use new API #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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