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

Migrate from slacker to slack-sdk #229

Open
wants to merge 7 commits into
base: develop
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
3 changes: 1 addition & 2 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
[bumpversion]
current_version = 1.0.0
current_version = 1.1.0
commit = True
tag = True
tag_name = {new_version}

[bumpversion:file:slackbot/VERSION]

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ slackbot_test_settings.py
/*.egg-info
.cache
/.vscode/
.idea
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
requests>=2.4.0
websocket-client>=0.22.0,<=0.44.0
slacker>=0.9.50
slack-sdk>=3.13.0
six>=1.10.0
pytest>=2.9.1
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
install_requires = (
'requests>=2.4.0',
'websocket-client>=0.22.0,<=0.44.0',
'slacker>=0.9.50',
'slack-sdk>=3.13.0',
'six>=1.10.0'
) # yapf: disable

Expand Down
2 changes: 1 addition & 1 deletion slackbot/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.0
1.1.0
61 changes: 41 additions & 20 deletions slackbot/slackclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import time
from ssl import SSLError

import slacker
import slack_sdk
from slack_sdk.http_retry.builtin_handlers import RateLimitErrorRetryHandler

from six import iteritems

from websocket import (
Expand Down Expand Up @@ -35,17 +37,21 @@ def __init__(self, token, timeout=None, bot_icon=None, bot_emoji=None, connect=T
self.rtm_start_args = rtm_start_args

if timeout is None:
self.webapi = slacker.Slacker(self.token)
self.webapi = slack_sdk.WebClient(self.token)
else:
self.webapi = slacker.Slacker(self.token, timeout=timeout)
self.webapi = slack_sdk.WebClient(self.token, timeout=timeout)

rate_limit_handler = RateLimitErrorRetryHandler(max_retry_count=100)
# Enable rate limited error retries
self.webapi.retry_handlers.append(rate_limit_handler)

if connect:
self.rtm_connect()

def rtm_connect(self):
reply = self.webapi.rtm.start(**(self.rtm_start_args or {})).body
time.sleep(1)
reply = self.webapi.rtm_connect()
self.parse_slack_login_data(reply)
self.connected = True

def reconnect(self):
while True:
Expand All @@ -61,22 +67,37 @@ def parse_slack_login_data(self, login_data):
self.login_data = login_data
self.domain = self.login_data['team']['domain']
self.username = self.login_data['self']['name']
self.parse_user_data(login_data['users'])
self.parse_channel_data(login_data['channels'])
self.parse_channel_data(login_data['groups'])
self.parse_channel_data(login_data['ims'])

proxy, proxy_port, no_proxy = get_http_proxy(os.environ)

self.websocket = create_connection(self.login_data['url'], http_proxy_host=proxy,
http_proxy_port=proxy_port, http_no_proxy=no_proxy)
self.websocket = create_connection(
self.login_data['url'],
http_proxy_host=proxy,
http_proxy_port=proxy_port,
http_no_proxy=no_proxy
)

self.websocket.sock.setblocking(0)

logger.debug('Getting users')
for page in self.webapi.users_list(limit=200):
self.parse_user_data(page['members'])
logger.debug('Getting channels')
for page in self.webapi.conversations_list(
exclude_archived=True,
types="public_channel,private_channel",
limit=1000
):
self.parse_channel_data(page['channels'])

def parse_channel_data(self, channel_data):
self.ping()
logger.debug('Adding %d channels', len(channel_data))
self.channels.update({c['id']: c for c in channel_data})

def parse_user_data(self, user_data):
self.ping()
logger.debug('Adding %d users', len(user_data))
self.users.update({u['id']: u for u in user_data})

def send_to_websocket(self, data):
Expand Down Expand Up @@ -126,34 +147,34 @@ def rtm_send_message(self, channel, message, attachments=None, thread_ts=None):

def upload_file(self, channel, fname, fpath, comment):
fname = fname or to_utf8(os.path.basename(fpath))
self.webapi.files.upload(fpath,
self.webapi.files_upload(file=fpath,
channels=channel,
filename=fname,
initial_comment=comment)

def upload_content(self, channel, fname, content, comment):
self.webapi.files.upload(None,
channels=channel,
self.webapi.files_upload(channels=channel,
content=content,
filename=fname,
initial_comment=comment)

def send_message(self, channel, message, attachments=None, as_user=True, thread_ts=None):
self.webapi.chat.post_message(
channel,
message,
def send_message(self, channel, message, attachments=None, blocks=None, as_user=True, thread_ts=None):
self.webapi.chat_postMessage(
channel=channel,
text=message,
username=self.login_data['self']['name'],
icon_url=self.bot_icon,
icon_emoji=self.bot_emoji,
attachments=attachments,
blocks=blocks,
as_user=as_user,
thread_ts=thread_ts)

def get_channel(self, channel_id):
return Channel(self, self.channels[channel_id])

def open_dm_channel(self, user_id):
return self.webapi.im.open(user_id).body["channel"]["id"]
return self.webapi.conversations_open(users=[user_id])["channel"]["id"]

def find_channel_by_name(self, channel_name):
for channel_id, channel in iteritems(self.channels):
Expand All @@ -173,7 +194,7 @@ def find_user_by_name(self, username):
return userid

def react_to_message(self, emojiname, channel, timestamp):
self.webapi.reactions.add(
self.webapi.reactions_add(
name=emojiname,
channel=channel,
timestamp=timestamp)
Expand Down
48 changes: 24 additions & 24 deletions tests/functional/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import json
import re
import time
import slacker
import slack_sdk
import websocket
import six
from six.moves import _thread, range
Expand All @@ -13,7 +13,7 @@ class Driver(object):
the tests code can concentrate on higher level logic.
"""
def __init__(self, driver_apitoken, driver_username, testbot_username, channel, private_channel):
self.slacker = slacker.Slacker(driver_apitoken)
self.webapi = slack_sdk.WebClient(driver_apitoken)
self.driver_username = driver_username
self.driver_userid = None
self.test_channel = channel
Expand All @@ -36,7 +36,7 @@ def start(self):
self._rtm_connect()
# self._fetch_users()
self._start_dm_channel()
self._join_test_channel()
# self._join_test_channel() # the underlying api method is no longer available

def wait_for_bot_online(self):
self._wait_for_bot_presense(True)
Expand Down Expand Up @@ -142,7 +142,7 @@ def ensure_reaction_posted(self, emojiname, maxwait=5):
def _send_message_to_bot(self, channel, msg):
self.clear_events()
self._start_ts = time.time()
self.slacker.chat.post_message(channel, msg, username=self.driver_username)
self.webapi.chat_postMessage(channel=channel, text=msg, username=self.driver_username)

def _wait_for_bot_message(self, channel, match, maxwait=60, tosender=True, thread=False):
for _ in range(maxwait):
Expand All @@ -157,10 +157,10 @@ def _has_got_message(self, channel, match, start=None, end=None):
match = six.text_type(r'\<@{}\>: {}').format(self.driver_userid, match)
oldest = start or self._start_ts
latest = end or time.time()
func = self.slacker.channels.history if channel.startswith('C') \
else self.slacker.im.history
func = self.webapi.channels_history if channel.startswith('C') \
else self.webapi.im_history
response = func(channel, oldest=oldest, latest=latest)
for msg in response.body['messages']:
for msg in response['messages']:
if msg['type'] == 'message' and re.match(match, msg['text'], re.DOTALL):
return True
return False
Expand All @@ -179,19 +179,19 @@ def _has_got_message_rtm(self, channel, match, tosender=True, thread=False):
return False

def _fetch_users(self):
response = self.slacker.users.list()
for user in response.body['members']:
response = self.webapi.users_list()
for user in response['members']:
self.users[user['name']] = user['id']

self.testbot_userid = self.users[self.testbot_username]
self.driver_userid = self.users[self.driver_username]

def _rtm_connect(self):
r = self.slacker.rtm.start().body
r = self.webapi.rtm_connect()
self.driver_username = r['self']['name']
self.driver_userid = r['self']['id']

self.users = {u['name']: u['id'] for u in r['users']}
self.users = {u['name']: u['id'] for u in self.webapi.users_list()['members']}
self.testbot_userid = self.users[self.testbot_username]

self._websocket = websocket.create_connection(r['url'])
Expand All @@ -217,18 +217,18 @@ def _rtm_read_forever(self):

def _start_dm_channel(self):
"""Start a slack direct messages channel with the test bot"""
response = self.slacker.im.open(self.testbot_userid)
self.dm_chan = response.body['channel']['id']
response = self.webapi.conversations_open(users=[self.testbot_userid])
self.dm_chan = response['channel']['id']

def _is_testbot_online(self):
response = self.slacker.users.get_presence(self.testbot_userid)
return response.body['presence'] == self.slacker.presence.ACTIVE
response = self.webapi.users_getPresence(user=self.testbot_userid)
return response['presence'] == 'active'

def _has_uploaded_file(self, name, start=None, end=None):
ts_from = start or self._start_ts
ts_to = end or time.time()
response = self.slacker.files.list(user=self.testbot_userid, ts_from=ts_from, ts_to=ts_to)
for f in response.body['files']:
response = self.webapi.files_list(user=self.testbot_userid, ts_from=ts_from, ts_to=ts_to)
for f in response['files']:
if f['name'] == name:
return True
return False
Expand All @@ -248,18 +248,18 @@ def _has_reacted(self, emojiname):
for event in self.events:
if event['type'] == 'reaction_added' \
and event['user'] == self.testbot_userid \
and (event.get('reaction', '') == emojiname \
and (event.get('reaction', '') == emojiname
or event.get('name', '') == emojiname):
return True
return False

def _join_test_channel(self):
response = self.slacker.channels.join(self.test_channel)
self.cm_chan = response.body['channel']['id']
response = self.webapi.channels_join(name=self.test_channel)
self.cm_chan = response['channel']['id']
self._invite_testbot_to_channel()

# Slacker/Slack API's still references to private_channels as 'groups'
private_channels = self.slacker.groups.list(self.test_private_channel).body['groups']
private_channels = self.webapi.groups_list(self.test_private_channel)['groups']
for private_channel in private_channels:
if self.test_private_channel == private_channel['name']:
self.gm_chan = private_channel['id']
Expand All @@ -270,12 +270,12 @@ def _join_test_channel(self):
self.test_private_channel))

def _invite_testbot_to_channel(self):
if self.testbot_userid not in self.slacker.channels.info(self.cm_chan).body['channel']['members']:
self.slacker.channels.invite(self.cm_chan, self.testbot_userid)
if self.testbot_userid not in self.webapi.channels_info(channel=self.cm_chan)['channel']['members']:
self.webapi.channels_invite(channel=self.cm_chan, user=self.testbot_userid)

def _invite_testbot_to_private_channel(self, private_channel):
if self.testbot_userid not in private_channel['members']:
self.slacker.groups.invite(self.gm_chan, self.testbot_userid)
self.webapi.groups_invite(channel=self.gm_chan, user=self.testbot_userid)

def _is_bot_message(self, msg):
if msg['type'] != 'message':
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/test_slackclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ def test_parse_user_data(slack_client):

def test_init_with_timeout():
client = SlackClient(None, connect=False)
assert client.webapi.api.timeout == 10 # seconds default timeout
assert client.webapi.timeout == 30 # seconds default timeout

expected_timeout = 42 # seconds
client = SlackClient(None, connect=False, timeout=expected_timeout)
assert client.webapi.api.timeout == expected_timeout
assert client.webapi.timeout == expected_timeout