Skip to content

Commit 6ab243f

Browse files
authored
Add user-agent headers (#160)
* Send user-agent header for Web API calls
1 parent c09dd4b commit 6ab243f

File tree

6 files changed

+112
-13
lines changed

6 files changed

+112
-13
lines changed

setup.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,29 @@
11
from setuptools import setup
2+
import codecs
3+
import os
4+
import re
5+
6+
here = os.path.abspath(os.path.dirname(__file__))
7+
8+
# Read the version number from a source file.
9+
# Why read it, and not import?
10+
# see https://groups.google.com/d/topic/pypa-dev/0PkjVpcxTzQ/discussion
11+
def find_version(*file_paths):
12+
# Open in Latin-1 so that we avoid encoding errors.
13+
# Use codecs.open for Python 2 compatibility
14+
with codecs.open(os.path.join(here, *file_paths), 'r', 'latin1') as f:
15+
version_file = f.read()
16+
17+
# The version line must have the form
18+
# __version__ = 'ver'
19+
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
20+
version_file, re.M)
21+
if version_match:
22+
return version_match.group(1)
23+
raise RuntimeError("Unable to find version string.")
224

325
setup(name='slackclient',
4-
version='1.0.4',
26+
version=find_version('slackclient', 'version.py'),
527
description='Python client for Slack.com',
628
url='http://github.com/slackapi/python-slackclient',
729
author='Ryan Huber',

slackclient/_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ def __init__(self, token):
2727
self.token = token
2828
self.server = Server(self.token, False)
2929

30+
def append_user_agent(self, name, version):
31+
self.server.append_user_agent(name, version)
32+
3033
def rtm_connect(self):
3134
'''
3235
Connects to the RTM Websocket

slackclient/_server.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ def __str__(self):
6161
def __repr__(self):
6262
return self.__str__()
6363

64+
def append_user_agent(self, name, version):
65+
self.api_requester.append_user_agent(name, version)
66+
6467
def rtm_connect(self, reconnect=False, timeout=None):
6568
reply = self.api_requester.do(self.token, "rtm.start", timeout=timeout)
6669
if reply.status_code != 200:

slackclient/_slackrequest.py

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,50 @@
33
import requests
44
import six
55

6+
import sys
7+
import platform
8+
from .version import __version__
9+
610

711
class SlackRequest(object):
12+
def __init__(self):
13+
14+
# __name__ returns 'slackclient._slackrequest', we only want 'slackclient'
15+
client_name = __name__.split('.')[0]
16+
client_version = __version__ # Version is returned from version.py
17+
18+
# Construct the user-agent header with the package info, Python version and OS version.
19+
self.default_user_agent = {
20+
"client": "{0}/{1}".format(client_name, client_version),
21+
"python": "Python/{v.major}.{v.minor}.{v.micro}".format(v=sys.version_info),
22+
"system": "{0}/{1}".format(platform.system(), platform.release())
23+
}
24+
25+
self.custom_user_agent = None
26+
27+
def get_user_agent(self):
28+
# Check for custom user-agent and append if found
29+
if self.custom_user_agent:
30+
custom_ua_list = ["/".join(client_info) for client_info in self.custom_user_agent]
31+
custom_ua_string = " ".join(custom_ua_list)
32+
self.default_user_agent['custom'] = custom_ua_string
833

9-
@staticmethod
10-
def do(token, request="?", post_data=None, domain="slack.com", timeout=None):
11-
'''
34+
# Concatenate and format the user-agent string to be passed into request headers
35+
ua_string = []
36+
for key, val in self.default_user_agent.items():
37+
ua_string.append(val)
38+
39+
user_agent_string = " ".join(ua_string)
40+
return user_agent_string
41+
42+
def append_user_agent(self, name, version):
43+
if self.custom_user_agent:
44+
self.custom_user_agent.append([name.replace("/", ":"), version.replace("/", ":")])
45+
else:
46+
self.custom_user_agent = [[name, version]]
47+
48+
def do(self, token, request="?", post_data=None, domain="slack.com", timeout=None):
49+
"""
1250
Perform a POST request to the Slack Web API
1351
1452
Args:
@@ -19,12 +57,12 @@ def do(token, request="?", post_data=None, domain="slack.com", timeout=None):
1957
{'channel': 'CABC12345'}
2058
domain (str): if for some reason you want to send your request to something other
2159
than slack.com
22-
'''
23-
post_data = post_data or {}
60+
"""
2461

2562
# Pull file out so it isn't JSON encoded like normal fields.
2663
# Only do this for requests that are UPLOADING files; downloading files
2764
# use the 'file' argument to point to a File ID.
65+
post_data = post_data or {}
2866
upload_requests = ['files.upload']
2967
files = None
3068
if request in upload_requests:
@@ -36,5 +74,6 @@ def do(token, request="?", post_data=None, domain="slack.com", timeout=None):
3674

3775
url = 'https://{0}/api/{1}'.format(domain, request)
3876
post_data['token'] = token
77+
headers = {'user-agent': self.get_user_agent()}
3978

40-
return requests.post(url, data=post_data, files=files, timeout=timeout)
79+
return requests.post(url, headers=headers, data=post_data, files=files, timeout=timeout)

slackclient/version.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# see: http://legacy.python.org/dev/peps/pep-0440/#public-version-identifiers
2+
__version__ = '1.0.4'

tests/test_slackrequest.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,73 @@
11
from slackclient._slackrequest import SlackRequest
2+
from slackclient.version import __version__
23
import json
34
import os
45

6+
def test_http_headers(mocker):
7+
requests = mocker.patch('slackclient._slackrequest.requests')
8+
request = SlackRequest()
9+
10+
request.do('xoxb-123', 'chat.postMessage', {'text': 'test', 'channel': '#general'})
11+
args, kwargs = requests.post.call_args
12+
13+
assert None != kwargs['headers']['user-agent']
14+
15+
def test_custom_user_agent(mocker):
16+
requests = mocker.patch('slackclient._slackrequest.requests')
17+
request = SlackRequest()
18+
19+
request.append_user_agent("fooagent1","0.1")
20+
request.append_user_agent("baragent/2","0.2")
21+
22+
request.do('xoxb-123', 'chat.postMessage', {'text': 'test', 'channel': '#general'})
23+
args, kwargs = requests.post.call_args
24+
25+
# Verify user-agent includes both default and custom agent info
26+
assert "slackclient/{}".format(__version__) in kwargs['headers']['user-agent']
27+
assert "fooagent1/0.1" in kwargs['headers']['user-agent']
28+
29+
# verify escaping of slashes in custom agent name
30+
assert "baragent:2/0.2" in kwargs['headers']['user-agent']
31+
532
def test_post_file(mocker):
633
requests = mocker.patch('slackclient._slackrequest.requests')
34+
request = SlackRequest()
735

8-
response = SlackRequest.do('xoxb-123',
36+
request.do('xoxb-123',
937
'files.upload',
1038
{'file': open(os.path.join('.', 'tests', 'data', 'slack_logo.png'), 'rb'),
1139
'filename': 'slack_logo.png'})
40+
args, kwargs = requests.post.call_args
1241

1342
assert requests.post.call_count == 1
14-
args, kwargs = requests.post.call_args
1543
assert 'https://slack.com/api/files.upload' == args[0]
1644
assert {'filename': 'slack_logo.png',
1745
'token': 'xoxb-123'} == kwargs['data']
1846
assert None != kwargs['files']
1947

2048
def test_get_file(mocker):
2149
requests = mocker.patch('slackclient._slackrequest.requests')
50+
request = SlackRequest()
2251

23-
SlackRequest.do('xoxb-123', 'files.info', {'file': 'myFavoriteFileID'})
52+
request.do('xoxb-123', 'files.info', {'file': 'myFavoriteFileID'})
53+
args, kwargs = requests.post.call_args
2454

2555
assert requests.post.call_count == 1
26-
args, kwargs = requests.post.call_args
2756
assert 'https://slack.com/api/files.info' == args[0]
2857
assert {'file': "myFavoriteFileID",
2958
'token': 'xoxb-123'} == kwargs['data']
3059
assert None == kwargs['files']
3160

3261
def test_post_attachements(mocker):
3362
requests = mocker.patch('slackclient._slackrequest.requests')
63+
request = SlackRequest()
3464

35-
SlackRequest.do('xoxb-123',
65+
request.do('xoxb-123',
3666
'chat.postMessage',
3767
{'attachments': [{'title': 'hello'}]})
68+
args, kwargs = requests.post.call_args
3869

3970
assert requests.post.call_count == 1
40-
args, kwargs = requests.post.call_args
4171
assert 'https://slack.com/api/chat.postMessage' == args[0]
4272
assert {'attachments': json.dumps([{'title': 'hello'}]),
4373
'token': 'xoxb-123'} == kwargs['data']

0 commit comments

Comments
 (0)