forked from opentok/Opentok-Python-SDK
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOpenTokSDK.py
More file actions
200 lines (160 loc) · 7.65 KB
/
OpenTokSDK.py
File metadata and controls
200 lines (160 loc) · 7.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
"""
OpenTok Python Library v0.91.0
http://www.tokbox.com/
Copyright 2011, TokBox, Inc.
"""
import urllib
import urllib2
import datetime
import calendar
import time
import hmac
import hashlib
import base64
import random
TIMEOUT = 10
class OpenTokException(BaseException):
"Generic OpenTok Error. All other errors extend this."
pass
class RequestError(OpenTokException):
"Indicates an error during the request. Most likely an error connecting to the OpenTok API servers. (HTTP 500 error)"
pass
class AuthError(OpenTokException):
"Indicates that the problem was likely with credentials. Check your API key and API secret and try again"
pass
class SessionProperties(object):
echoSuppression_enabled = None
multiplexer_numOutputStreams = None
multiplexer_switchType = None
multiplexer_switchTimeout = None
p2p_preference = None
def __iter__(self):
d= {'echoSuppression.enabled' : self.echoSuppression_enabled,
'multiplexer.numOutputStreams' : self.multiplexer_numOutputStreams,
'multiplexer.switchType' : self.multiplexer_switchType,
'multiplexer.switchTimeout' : self.multiplexer_switchTimeout,
'p2p.preference' : self.p2p_preference,
}
return d.iteritems()
class RoleConstants:
"List of valid roles for a token"
SUBSCRIBER = "subscriber" #Can only subscribe
PUBLISHER = "publisher" #Can publish, subscribe, and signal
MODERATOR = "moderator" #Can do the above along with forceDisconnect and forceUnpublish
class OpenTokSession(object):
def __init__(self, session_id):
self.session_id = session_id
class OpenTokSDK(object):
"""
Use this SDK to create tokens and interface with the server-side portion of the Opentok API.
"""
TOKEN_SENTINEL = "T1=="
API_URL = "http://staging.tokbox.com/hl"
# Uncomment this line when you launch your app
# API_URL = "https://api.opentok.com/hl";
def __init__(self, api_key, api_secret):
self.api_key = api_key
self.api_secret = api_secret.strip()
def generate_token(self, session_id=None, role=None, expire_time=None, connection_data=None, **kwargs):
"""
Generate a token which is passed to the JS API to enable widgets to connect to the Opentok api.
session_id: Specify a session_id to make this token only valid for that session_id.
role: One of the constants defined in RoleConstants. Default is publisher, look in the documentation to learn more about roles.
expire_time: Integer timestamp. You can override the default token expire time of 24h by choosing an explicit expire time. Can be up to 7d after create_time.
"""
create_time = datetime.datetime.utcnow()
if session_id is None:
session_id = ''
if not role:
role = RoleConstants.PUBLISHER
if role != RoleConstants.SUBSCRIBER and \
role != RoleConstants.PUBLISHER and \
role != RoleConstants.MODERATOR:
raise OpenTokException("%s is not a valid role" % role)
data_params = dict(session_id=session_id,
create_time=calendar.timegm(create_time.timetuple()),
role=role,
)
if expire_time is not None:
if isinstance(expire_time, datetime.datetime):
data_params['expire_time'] = calendar.timegm(expire_time.timetuple())
else:
try:
data_params['expire_time'] = int(expire_time)
except ValueError, TypeError:
raise OpenTokException("Expire time must be a number")
if data_params['expire_time'] < time.time():
raise OpenTokException("Expire time must be in the future")
if data_params['expire_time'] > time.time() + 604800:
raise OpenTokException("Expire time must be in the next 7 days")
if connection_data is not None:
if len(connection_data) > 1000:
raise OpenTokException("Connection data must be less than 1000 characters")
data_params['connection_data'] = connection_data
data_params['nonce'] = random.randint(0,999999)
data_string = urllib.urlencode(data_params, True)
sig = self._sign_string(data_string, self.api_secret)
token_string = "%s%s" % (self.TOKEN_SENTINEL, base64.b64encode("partner_id=%s&sig=%s:%s" % (self.api_key, sig, data_string)))
return token_string
def create_session(self, location='', properties={}, **kwargs):
"""
Create a new session in the OpenTok API. Returns an OpenTokSession object with a session_id property.
location: IP address of the user requesting the session. This is used for geolocation to choose which datacenter the session will live on.
properties: An instance of the SessionProperties object. Fill in the fields that you are interested in to use features of the groups API. Look in the documentation for more details. Also accepts any dict-like object.
"""
#ip_passthru is a deprecated argument and has been replaced with location
if 'ip_passthru' in kwargs:
location = kwargs['ip_passthru']
params = dict(api_key=self.api_key)
params['location'] = location
params.update(properties)
dom = ''
try:
dom = self._do_request("/session/create", params)
except RequestError:
raise
except Exception, e:
raise RequestError("Failed to create session: %s" % str(e) )
try:
error = dom.getElementsByTagName('error')
if error:
error = error[0]
raise AuthError("Failed to create session (code=%s): %s" % (error.attributes['code'].value, error.firstChild.attributes['message'].value))
session_id = dom.getElementsByTagName('session_id')[0].childNodes[0].nodeValue
return OpenTokSession(session_id)
except Exception, e:
raise OpenTokException("Failed to generate session: %s" % str(e))
def _sign_string(self, string, secret):
return hmac.new(secret, string.encode("utf-8"), hashlib.sha1).hexdigest()
def _do_request(self, url, params):
import xml.dom.minidom as xmldom
if '_token' in params: #Do token auth if _token is present, partner auth normally
auth_header = ('X-TB-TOKEN-AUTH', params['_token'])
del params['_token']
else:
auth_header = ('X-TB-PARTNER-AUTH', "%s:%s" % (self.api_key, self.api_secret))
method = "POST" if params else "GET"
data_string = urllib.urlencode(params, True)
context_source = [
('method', method),
('Content-Type', 'application-xml'),
('Content-Length', len(data_string)),
auth_header
]
req_string = self.API_URL + url
try:
opener = urllib2.build_opener()
opener.addheaders = context_source
if data_string:
request = urllib2.Request(url=req_string, data=data_string)
else: #GET if no data_string
request = urllib2.Request(url=req_string)
try:
response = opener.open(request, timeout=TIMEOUT)
except TypeError: #Python2.6 added the timeout keyword, if it doesn't get accepted, try without it
response = opener.open(request)
dom = xmldom.parseString(response.read())
response.close()
except urllib2.HTTPError, e:
raise RequestError("Failed to send request: %s" % str(e))
return dom