Skip to content

Commit 65a6496

Browse files
committed
Implement the sync
1 parent 09d80be commit 65a6496

File tree

3 files changed

+76
-5
lines changed

3 files changed

+76
-5
lines changed
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
11
# -*- coding: utf-8 -*-
22
from django.core.management.base import BaseCommand, CommandError
3+
from slacksync.membersync import SlackMemberSync
4+
from slacksync.utils import api_configured
35

46

57
class Command(BaseCommand):
68
help = 'Make sure all members are in Slack and optionally kick non-members'
79

810
def add_arguments(self, parser):
11+
parser.add_argument('--autodeactivate', action='store_true', help='Automatically deactivate users that are no longer members')
12+
913
pass
1014

1115
def handle(self, *args, **options):
12-
raise NotImplemented()
16+
if not api_configured():
17+
raise CommandError("API not configured")
18+
autoremove = False
19+
if options['autodeactivate']:
20+
autoremove = True
21+
sync = SlackMemberSync(autoremove)
22+
tbd = sync.sync_members()
23+
if options['verbosity'] > 1:
24+
for dm in tbd:
25+
if autoremove:
26+
print("User {uid} ({email}) was removed".format(uid=dm[0], email=dm[1]))
27+
else:
28+
print("User {uid} ({email}) should be removed".format(uid=dm[0], email=dm[1]))

project/slacksync/membersync.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
# -*- coding: utf-8 -*-
2+
import logging
3+
import time
4+
25
from django.conf import settings
3-
from members.objects import Member
6+
from members.models import Member
47
from requests.sessions import Session
58

69
from .utils import api_configured, get_client
710

8-
logger = logger.getLogger()
11+
logger = logging.getLogger()
912

1013

1114
class SlackMemberSync(object):
15+
"""Sync members and slack members"""
16+
1217
def get_slack_users_simple(self, slack, exclude_api_user=True):
18+
"""Get just the properties we need from the slack members list"""
1319
response = slack.users.list()
1420
emails = []
1521
for member in response.body['members']:
@@ -24,16 +30,43 @@ def get_slack_users_simple(self, slack, exclude_api_user=True):
2430
def sync_members(self, autodeactivate=False):
2531
"""Sync members, NOTE: https://github.com/ErikKalkoken/slackApiDoc/blob/master/users.admin.setInactive.md says
2632
deactivation via API works only on paid tiers"""
33+
if not api_configured():
34+
raise RuntimeError("Slack API not configured")
2735
with Session() as session:
2836
slack = get_client(session=session)
2937
slack_users = self.get_slack_users_simple(slack)
30-
slac_emails = [x[2] for x in slack_users]
38+
slack_emails = set([x[2] for x in slack_users])
3139
add_members = Member.objects.exclude(email__in=slack_emails)
3240
for member in add_members:
3341
try:
3442
resp = slack.users.admin.invite(member.email)
3543
if 'ok' not in resp.body or not resp.body['ok']:
3644
self.logger.error("Could not invite {}, response: {}".format(member.email, response.body))
45+
time.sleep(0.1) # rate-limit
3746
except Exception as e:
3847
logger.exception("Got exception when trying to invite {}".format(member.email))
39-
# TODO: check which members should be removed
48+
49+
member_emails = set(Member.objects.values_list('email', flat=True))
50+
remove_slack_emails = slack_emails - member_emails
51+
remove_usernames = []
52+
if not remove_slack_emails:
53+
return remove_usernames
54+
55+
usernames_by_email = {x[2]: x[1] for x in slack_users}
56+
remove_usernames = [(usernames_by_email[x], x) for x in remove_slack_emails]
57+
58+
if not autodeactivate:
59+
return remove_usernames
60+
61+
userids_by_email = {x[2]: x[0] for x in slack_users}
62+
for email in remove_slack_emails:
63+
try:
64+
resp = slack.users.admin.setInactive(userids_by_email[email])
65+
if 'ok' not in resp.body or not resp.body['ok']:
66+
self.logger.error(
67+
"Could not deactivate {}, response: {}".format(email, response.body))
68+
time.sleep(0.1) # rate-limit
69+
except Exception as e:
70+
logger.exception("Got exception when trying to deactivate {}".format(email))
71+
72+
return remove_usernames

project/slacksync/utils.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
11
# -*- coding: utf-8 -*-
2+
import logging
3+
24
from django.conf import settings
35
from slacker import Slacker
46

7+
logger = logging.getLogger()
8+
59

610
def api_configured():
11+
"""Check that Slack API settings are configured"""
712
return bool(settings.SLACK_APIKEY) and bool(settings.SLACK_API_USERNAME)
813

914

1015
def get_client(**kwargs):
16+
"""Get a Slacker instance"""
1117
if not api_configured():
1218
return False
1319
return Slacker(settings.SLACK_APIKEY, **kwargs)
20+
21+
22+
def quick_invite(email):
23+
"""Quickly invite single email"""
24+
if not api_configured():
25+
return False
26+
slack = get_client()
27+
try:
28+
resp = slack.users.admin.invite(member.email)
29+
if 'ok' not in resp.body or not resp.body['ok']:
30+
self.logger.error("Could not invite {}, response: {}".format(email, response.body))
31+
return False
32+
except Exception as e:
33+
logger.exception("Got exception when trying to invite {}".format(email))
34+
return False
35+
return True

0 commit comments

Comments
 (0)