Skip to content

Commit

Permalink
Add Slack importer bot.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomasz Kolek authored and timabbott committed May 9, 2017
1 parent 49fec57 commit 61d5d41
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 0 deletions.
127 changes: 127 additions & 0 deletions api/integrations/slack/zulip_slack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#
# slacker is a dependency for this script.
#

from __future__ import absolute_import
from __future__ import print_function

import sys
import string
import random
from six.moves import range
from typing import List, Dict

import zulip
from slacker import Slacker, Response, Error as SlackError

import zulip_slack_config as config


client = zulip.Client(email=config.ZULIP_USER, api_key=config.ZULIP_API_KEY, site=config.ZULIP_SITE)


class FromSlackImporter(object):
def __init__(self, slack_token, get_archived_channels=True):
# type: (str, bool) -> None
self.slack = Slacker(slack_token)
self.get_archived_channels = get_archived_channels

self._check_slack_token()

def get_slack_users_email(self):
# type: () -> Dict[str, Dict[str, str]]

r = self.slack.users.list()
self._check_if_response_is_successful(r)
results_dict = {}
for user in r.body['members']:
if user['profile'].get('email') and user.get('deleted') is False:
results_dict[user['id']] = {'email': user['profile']['email'], 'name': user['profile']['real_name']}
return results_dict

def get_slack_public_channels_names(self):
# type: () -> List[Dict[str, str]]

r = self.slack.channels.list()
self._check_if_response_is_successful(r)
return [{'name': channel['name'], 'members': channel['members']} for channel in r.body['channels']]

def get_slack_private_channels_names(self):
# type: () -> List[str]

r = self.slack.groups.list()
self._check_if_response_is_successful(r)
return [
channel['name'] for channel in r.body['groups']
if not channel['is_archived'] or self.get_archived_channels
]

def _check_slack_token(self):
# type: () -> None
try:
r = self.slack.api.test()
self._check_if_response_is_successful(r)
except SlackError as e:
print(e)
sys.exit(1)
except Exception as e:
print(e)
sys.exit(1)

def _check_if_response_is_successful(self, response):
# type: (Response) -> None
print(response)
if not response.successful:
print(response.error)
sys.exit(1)

def _generate_random_password(size=10):
# type: (int) -> str
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(size))

def get_and_add_users(slack_importer):
# type: (Slacker) -> Dict[str, Dict[str, str]]
users = slack_importer.get_slack_users_email()
added_users = {}
print('######### IMPORTING USERS STARTED #########\n')
for user_id, user in users.items():
r = client.create_user({
'email': user['email'],
'full_name': user['name'],
'short_name': user['name']
})
if not r.get('msg'):
added_users[user_id] = user
print(u"{} -> {}\nCreated\n".format(user['name'], user['email']))
else:
print(u"{} -> {}\n{}\n".format(user['name'], user['email'], r.get('msg')))
print('######### IMPORTING USERS FINISHED #########\n')
return added_users

def create_streams_and_add_subscribers(slack_importer, added_users):
# type: (Slacker, Dict[str, Dict[str, str]]) -> None
channels_list = slack_importer.get_slack_public_channels_names()
print('######### IMPORTING STREAMS STARTED #########\n')
for stream in channels_list:
subscribed_users = [added_users[member]['email'] for member in stream['members'] if member in added_users.keys()]
if subscribed_users:
r = client.add_subscriptions([{"name": stream['name']}], principals=subscribed_users)
if not r.get('msg'):
print(u"{} -> created\n".format(stream['name']))
else:
print(u"{} -> {}\n".format(stream['name'], r.get('msg')))
else:
print(u"{} -> wasn't created\nNo subscribers\n".format(stream['name']))
print('######### IMPORTING STREAMS FINISHED #########\n')

def main():
# type: () -> None
importer = FromSlackImporter(config.SLACK_TOKEN)
added_users = get_and_add_users(importer)
create_streams_and_add_subscribers(importer, added_users)

if __name__ == '__main__':
main()
34 changes: 34 additions & 0 deletions api/integrations/slack/zulip_slack_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2014 Zulip, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


### REQUIRED CONFIGURATION ###

# Change these values to your Slack credentials.
SLACK_TOKEN = 'slack_token'

# Change these values to the credentials for your Slack bot.
ZULIP_USER = '[email protected]'
ZULIP_API_KEY = 'user-email_api_key'

# Set this to your Zulip API server URI
ZULIP_SITE = 'https://zulip.example.com'
44 changes: 44 additions & 0 deletions templates/zerver/help/import-users-and-channels-from-slack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Import users and channels from Slack

{!follow-steps.md!} import users and channels from Slack to Zulip.

!!! warn ""
**Note:** Please ensure that you have admin rights before importing users and channels from Slack.

1. Generate a Slack API token using Slack's [test token generator](https://api.slack.com/docs/oauth-test-tokens)
to import all of the necessary data.

{!go-to-the.md!} [Your bots](/#settings/your-bots)
{!settings.md!}

3. Click on the **Show/change your API key** button.

4. Upon clicking the **Show/change your API key** button,
you will be asked to confirm your identity by entering
your password in the **Current password** field.

5. Click the **Get API Key** button and copy the generated API Key.

6. Fill all of the settings in `api/integrations/slack/zulip_slack_config.py`:

* `SLACK_TOKEN` - the token from point number 1.

* `ZULIP_USER` - the e-mail of the user (the user that API key was generated for).

* `ZULIP_KEY` - the API key from point number 4.

* `ZULIP_SITE` - the Zulip API server URI.

7. Install the `slacker` dependency using the command `pip install slacker`

8. Finally, run the script in your local Zulip directory using the command
`python api/integrations/slack/zulip_slack.py`

## Importing users from a different organization

If the users are not from the same organization, you should change your organization settings accordingly.

{!go-to-the.md!} [Organization settings](/#administration/organization-settings)
{!admin.md!}

2. Disable the **New users restricted to the following domains** option.
1 change: 1 addition & 0 deletions templates/zerver/help/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ as an **organization**.
## Tools & Customization
* [Keyboard shortcuts](/help/keyboard-shortcuts)
* [Add a bot or integration](/help/add-a-bot-or-integration)
* [Import users and channels from Slack](/help/import-users-and-channels-from-slack)

## Misc
* [Tips for Zulip on Windows](/help/zulip-on-windows)
Expand Down

0 comments on commit 61d5d41

Please sign in to comment.