def on_status(self, status): search_params = {} for word in status.text.split(" "): results = [k for k, v in dict.iteritems() if word.capitalize() in v] if results: search_params.setdefault(results[0], []).append(word.capitalize()) if search_params: data = urlencode({"q": search_params, "limit": 5, "order":"rolling_rank DESC"}) response = requests.get(url + "?" + data) if response.ok: if response.json(): tweet_positive_result(response, status) else: tweet_random_result(status) else: response.raise_for_status() else: search_params['search'] = [status.text.replace("#marmomood", "")] data = urlencode({"q": search_params, "limit": 1, "order":"rolling_rank DESC"}) response = requests.get(url + "?" + data) if response.ok: if response.json(): tweet_positive_result(response, status) else: tweet_random_result(status) else: response.raise_for_status()
def search_articles(self, phrase, **kwargs): """Search article matching phrase.""" for key in kwargs: if key not in ['status', '_fields', 'limit']: raise InvalidArg(key) return self.__call(self.knowledgeowl.suggest, 'GET', data=urlencode({'phrase': phrase}), params=urlencode(kwargs)).json()
def call(self, method, params1=None, params2=None, params3=None, params4=None): """Call Bitrix24 API method :param method: Method name :param params1: Method parameters 1 :param params2: Method parameters 2. Needed for methods with determinate consequence of parameters :param params3: Method parameters 3. Needed for methods with determinate consequence of parameters :param params4: Method parameters 4. Needed for methods with determinate consequence of parameters :return: Call result """ if method == '' or not isinstance(method, str): raise Exception('Empty Method') if method == 'batch' and 'prepared' not in params1: params1['cmd'] = self.prepare_batch(params1['cmd']) params1['prepared'] = True encoded_parameters = '' # print params1 for i in [params1, params2, params3, params4]: if i is not None: if 'cmd' in i: i = dict(i) encoded_parameters += self.encode_cmd(i['cmd']) + '&' + urlencode({'halt': i['halt']}) + '&' elif 'filter' in i: # i = dict(i) encoded_parameters += urlencode(i) else: encoded_parameters += urlencode(i) + '&' r = {} try: # request url url = self.get_url(method) # Make API request r = post(url, data=encoded_parameters, timeout=self.timeout) # Decode response result = loads(r.text) except ValueError: result = dict(error='Error on decode api response [%s]' % r.text) except exceptions.ReadTimeout: result = dict(error='Timeout waiting expired [%s sec]' % str(self.timeout)) except exceptions.ConnectionError: result = dict(error='Max retries exceeded [' + str(adapters.DEFAULT_RETRIES) + ']') if 'error' in result and result['error'] in ('NO_AUTH_FOUND', 'expired_token'): result = self.refresh_tokens() if result is not True: return result # Repeat API request after renew token result = self.call(method, params1, params2, params3, params4) elif 'error' in result and result['error'] in ('QUERY_LIMIT_EXCEEDED',): # Suspend call on two second, wait for expired limitation time by Bitrix24 API print('SLEEP =)') sleep(2) return self.call(method, params1, params2, params3, params4) return result
def send(self): """ Send the request to the remote url. :return: """ client = self.client get_params = client.params_get request_url = client.url.rstrip('/') if get_params: sorted_get_params = OrderedDict(sorted(get_params.items())) query_string = urlencode(sorted_get_params) if query_string: request_url += '?' + query_string self._sign(request_url) if client.is_put_method() or client.is_delete_method(): client.headers['X-HTTP-Method-Override'] = client.method.upper() response = self._make_request() return response
def bitrix_request(method, params, rec=True, post=True): url = _main_url.replace('METHOD', method) # url = url.replace('AUTH', _token['access_token']) params['auth'] = _token['access_token'] if post: req = requests.post(url, data=json.dumps(params, default=json_serial), headers=headers) else: req = requests.get(url, params=urlencode(params)) if req.status_code == 401: if rec: update_token() time.sleep(10) return bitrix_request(method, params, rec=False) else: raise Exception('401 error while requesting {}'.format(url)) elif req.status_code == 400: logging.error('Wrong data, {}'.format(req.content)) elif req.status_code != 200: time.sleep(10) if rec: return bitrix_request(method, params, rec=False) else: logging.info('{} error while requesting a {}'.format( req.status_code, url)) logging.info('Second try was failed, possible data loss') # return json.loads(req.content.decode('utf-8')) return req.json()
def get_article(self, article_id, **kwargs): """Retrieve article and it's current version html.""" for key in kwargs: if key not in ['replaceSnippets', '_fields']: raise InvalidArg(key) return self.__call(self.knowledgeowl.article(article_id), 'GET', data=json.dumps({'_fields': 'current_version'}), params=urlencode(kwargs))
def call(self, method, params1=None, params2=None, params3=None, params4=None): """Call Bitrix24 API method :param method: Method name :param params1: Method parameters 1 :param params2: Method parameters 2. Needed for methods with determinate consequence of parameters :param params3: Method parameters 3. Needed for methods with determinate consequence of parameters :param params4: Method parameters 4. Needed for methods with determinate consequence of parameters :return: Call result """ if method == '' or not isinstance(method, str): raise Exception('Empty Method') encoded_parameters = '' for i in [ params1, params2, params3, params4, { 'auth': self.auth_token } ]: if i is not None: encoded_parameters += urlencode(i) + '&' r = {} try: # request url url = self.api_url % (self.domain, self.high_level_domain, method) # Make API request r = post(url, params=encoded_parameters, timeout=self.timeout) # Decode response result = loads(r.text) except ValueError: result = dict(error='Error on decode api response [' + r.text + ']') except exceptions.ReadTimeout: result = dict(error='Timeout waiting expired [' + str(self.timeout) + ' sec]') except exceptions.ConnectionError: result = dict(error='Max retries exceeded [' + str(adapters.DEFAULT_RETRIES) + ']') if 'error' in result and result['error'] in ('NO_AUTH_FOUND', 'expired_token'): result = self.refresh_tokens() if result is not True: return result # Repeat API request after renew token result = self.call(method, params1, params2, params3, params4) elif 'error' in result and result['error'] in 'QUERY_LIMIT_EXCEEDED': # Suspend call on two second, wait for expired limitation time by Bitrix24 API sleep(2) return self.call(method, params1, params2, params3, params4) return result
def list_articles(self, **kwargs): """List all articles.""" m_keys = list(ARTICLE_KEYS) m_keys.extend(['sort', 'limit']) for key in kwargs: if key not in m_keys: raise InvalidArg(key) return self.__call(self.knowledgeowl.article, 'GET', params=urlencode(kwargs))
def encode_cmd(cmd): """Resort batch cmd by request keys and encode it :param cmd: dict List methods for batch request with request ids :return: str """ cmd_encoded = '' for i in sorted(cmd.keys()): cmd_encoded += urlencode({'cmd': {i: cmd[i]}}) + '&' return cmd_encoded
def tweet_random_result(status): data = urlencode({"limit": 10, "order":"rolling_rank DESC"}) response = requests.get(url + "?" + data) if response.ok: if response.json(): random_track = random.choice(response.tracks()) message = '''@{0} I couldn't find that. Why don't you check out "{1}," it's one of our hottest songs: {2}'''.format(status.user.screen_name, random_track.title, shorten_url(random_track.url)) api.update_status(shorten_message(message, random_track), in_reply_to_status_id = status.id) else: message = "@{0} your search was so specific, I couldn't find anything. Please try again.".format(status.user.screen_name) api.update_status(message, in_reply_to_status_id = status.id) else: response.raise_for_status()
def on_status(self, status): search_params = {} for word in status.text.split(" "): results = [ k for k, v in dict.iteritems() if word.capitalize() in v ] if results: search_params.setdefault(results[0], []).append(word.capitalize()) if search_params: data = urlencode({ "q": search_params, "limit": 5, "order": "rolling_rank DESC" }) response = requests.get(url + "?" + data) if response.ok: if response.json(): tweet_positive_result(response, status) else: tweet_random_result(status) else: response.raise_for_status() else: search_params['search'] = [status.text.replace("#marmomood", "")] data = urlencode({ "q": search_params, "limit": 1, "order": "rolling_rank DESC" }) response = requests.get(url + "?" + data) if response.ok: if response.json(): tweet_positive_result(response, status) else: tweet_random_result(status) else: response.raise_for_status()
def buildHttpQuery(self, taxonomy, parameters): if taxonomy.startswith('/'): taxonomy = taxonomy[1:] if not self.baseurl.endswith('/'): self.baseurl += '/' url = urljoin(self.baseurl, taxonomy) url_parts = list(urlparse(url)) query = dict(parse_qsl(url_parts[4])) query.update(parameters) url_parts[4] = urlencode(query) url = urlunparse(url_parts) return url
def tweet_random_result(status): data = urlencode({"limit": 10, "order": "rolling_rank DESC"}) response = requests.get(url + "?" + data) if response.ok: if response.json(): random_track = random.choice(response.tracks()) message = '''@{0} I couldn't find that. Why don't you check out "{1}," it's one of our hottest songs: {2}'''.format( status.user.screen_name, random_track.title, shorten_url(random_track.url)) api.update_status(shorten_message(message, random_track), in_reply_to_status_id=status.id) else: message = "@{0} your search was so specific, I couldn't find anything. Please try again.".format( status.user.screen_name) api.update_status(message, in_reply_to_status_id=status.id) else: response.raise_for_status()
def prepare_batch(params): """ Prepare methods for batch call :param params: dict :return: dict """ if not isinstance(params, dict): raise Exception('Invalid \'cmd\' structure') batched_params = dict() for call_id in sorted(params.keys()): if not isinstance(params[call_id], list): raise Exception('Invalid \'cmd\' method description') method = params[call_id].pop(0) if method == 'batch': raise Exception('Batch call cannot contain batch methods') temp = '' for i in params[call_id]: temp += urlencode(i) + '&' batched_params[call_id] = method + '?' + temp return batched_params
def call(self, method, params1=None, params2=None, params3=None, params4=None): """Call Bitrix24 API method :param method: Method name :param params1: Method parameters 1 :param params2: Method parameters 2. Needed for methods with determinate consequence of parameters :param params3: Method parameters 3. Needed for methods with determinate consequence of parameters :param params4: Method parameters 4. Needed for methods with determinate consequence of parameters :return: Call result """ # Checking token exists if not self.access_token: if self.refresh_token: self.refresh_tokens() else: self.authenticate() if method == '' or not isinstance(method, str): raise Exception('Empty Method') if method == 'batch' and 'prepared' not in params1: params1['cmd'] = self.prepare_batch(params1['cmd']) params1['prepared'] = True encoded_parameters = '' # print params1 for i in [ params1, params2, params3, params4, { 'auth': self.access_token } ]: if i is not None: if 'cmd' in i: i = dict(i) encoded_parameters += self.encode_cmd( i['cmd']) + '&' + urlencode({'halt': i['halt']}) + '&' else: encoded_parameters += urlencode(i) + '&' r = {} try: # request url url = self.api_url % (self.domain, method) # Make API request r = requests.post(url, data=encoded_parameters, timeout=self.timeout) # Decode response result = r.json() except ValueError: result = dict(error='Error on decode api response [%s]' % r.text) except requests.ReadTimeout: result = dict(error='Timeout waiting expired [%s sec]' % str(self.timeout)) except requests.ConnectionError as e: print(e.request.url) print(e) result = dict(error='Max retries exceeded [1]') except (AuthenticationFailed, TokenRenewFailed) as e: result = e.result if 'error' in result: error_code = result['error'] if error_code in ('invalid_token', 'NO_AUTH_FOUND', 'expired_token'): self.refresh_tokens() elif error_code in 'QUERY_LIMIT_EXCEEDED': # Suspend call on two second, wait limitation time by Bitrix24 API time.sleep(2) return self.call(method, params1, params2, params3, params4) # Repeat API request after renew token result = self.call(method, params1, params2, params3, params4) return result
def test_basic(): """Verify that urlencode works with four levels.""" d = {"a": {"b": {"c": "d"}}} expected = urllib.quote("a[b][c]=d", safe="=/&") assert urlencode(d) == expected
def test_with_non_dict(): """Verify that we raise an exception when passing a non-dict.""" with pytest.raises(TypeError): urlencode("e")
def test_with_list_value(): """Verify that urlencode works with list value.""" d = {'a': {"b": [1, 2, 3]}} expected = "a[b][]=1&a[b][]=2&a[b][]=3" assert urllib.unquote(urlencode(d)) == expected
def test_two(): """Verify that urlencode works with two params.""" d = {'a': 'b', 'c': {'d': 'e'}} expected = urllib.quote("a=b&c[d]=e", safe="=/&") assert urlencode(d) == expected
def test_no_array_braces(): """Verify that array braces can be left off.""" d = {'a': {"b": [1, 2, 3]}} expected = "a[b]=1&a[b]=2&a[b]=3" assert unquote(urlencode(d, array_braces=False)) == expected
def test_with_list_value(): """Verify that urlencode works with list value.""" d = {'a': {"b": [1, 2, 3]}} expected = "a[b][]=1&a[b][]=2&a[b][]=3" assert unquote(urlencode(d)) == expected
def test_key_types(): """Verify that urlencode works with key type 'int'.""" d = {1: {2: {3: 4}}} expected = quote("1[2][3]=4", safe="=/&") assert urlencode(d) == expected
def test_encode_list_key(): """Verify that list indexes are explicitly added.""" d = {'a': {"b": [1, 2, 3]}} expected = "a[b][0]=1&a[b][1]=2&a[b][2]=3" assert unquote(urlencode(d, encode_list_key=True)) == expected
def __await__(self): return self.api_call(self.url + urlencode(self.params)).__await__()
def __request(self, type='GET', content_type='application/json', dry_run=False, params={}, max_401_retry=3, **kwargs): self.type = str.upper(type) if dry_run is True: logging.debug("This is a dry run mode, skipping %s %s" % (self.type, self.endpoint)) logging.debug("Parameters: %s" % params) return "{'message': 'Dry-run mode, request " "%s %s skipped'}" % ( self.type, self.endpoint) logging.debug('%s %s' % (self.type, self.endpoint)) if params is not None: logging.debug('Input params: %s' % params) request_arguments = { 'params': params.copy(), 'verify': False, 'data': {} } if 'metaData' in request_arguments['params']: request_arguments['params']['metaData'] = json.dumps( request_arguments['params']['metaData']) if 'complex' in request_arguments['params']: try: from multidimensional_urlencode import urlencode logging.debug('complex key found, doing dirty stuff...') complex = urlencode( {'complex': request_arguments['params']['complex']}) self.endpoint += '?%s' % complex del (request_arguments['params']['complex']) except ImportError as e: logging.debug( "Unable to handle complex key, missing or" "failed to load multidimensionnal_urlencode: %s" % e) raise ValueError("Unable to handle complex filter") # XXX: To simplify method calling we only support 'params' as keyword. # In case of POST/PUT/DELETE we set params as 'data' keyword of # requests library. In case of file uploading, symbolised by the # presence of 'files' keyword we let params because contacts API does # not support files upload (POST) with parameters in body. if self.type.upper() != 'GET' and 'files' not in kwargs: action = None if 'action' in request_arguments['params']: action = request_arguments['params']['action'] del (request_arguments['params']['action']) request_arguments['data'] = json.dumps( dict(request_arguments['data'], **request_arguments.pop('params'))) request_arguments['headers'] = {'Content-type': content_type} if action is not None: logging.debug("Forcing action parameter to be passed in " "request URL") request_arguments['params'] = {'action': action} arguments = dict( list(request_arguments.items()) + list(kwargs.items())) if self.auth is not None: arguments['auth'] = self.auth logging.debug("Requests args: %s" % arguments) response = getattr(requests, str.lower(self.type))(self.endpoint, **arguments) logging.debug("URL: %s" % response.url) try: # Raise an exception onr 4xx, 5xx HTTPError response.raise_for_status() except Exception as e: def process_http_response(response): if response.status_code == 500: error = '{"status": 500, "message": "Internal Server '\ 'Error"}' else: error = json.loads(response.text) if 'message' in error: details = "" if 'details' in error: details = "(%s)" % error['details'] message = "HTTP code <%s>: %s %s" % (response.status_code, error, details) else: # this is an internal error message = e.message return message # manage 401 if response.status_code == 401: retry = max_401_retry - 1 logging.debug("401 'Unauthorized' status code returned, " "retrying a maximum of %s times" % max_401_retry) while response.status_code == 401 and retry >= 0: if hasattr(self.auth, 'token'): logging.debug("It looks like authentication mecanism " "uses token") self.auth.token.expire() response = getattr(requests, str.lower(self.type))(self.url, **arguments) logging.debug("Retry %s/%s: call to %s returned %s" % (max_401_retry - retry, max_401_retry, response.url, response.status_code)) retry -= 1 try: response.raise_for_status() except Exception as e: message = process_http_response(response) logging.debug(message) raise Exception(message) else: message = process_http_response(response) raise Exception(message) logging.debug("HTTP code <%s>: OK" % response.status_code) text = response.text or '{}' return json.loads(text)
def list_comments(self, **kwargs): for key in kwargs: if key not in ['article_id', '_fields', 'sort', 'limit']: raise InvalidArg(key) return self.__call(self.knowledgeowl.comment, 'GET', params=urlencode(kwargs))
def list_readerroles(self, _fields=None, sort=None, limit=None, **params): for key in kwargs: if key not in READERROLES_KEYS or key not in ['_fields', 'sort', 'limit']: raise InvalidArg(key) kwargs['project_id'] = self.project_id return self.__call(self.knowledgeowl.readerroles, 'GET', params=urlencode(kwargs))
def get_readerroles(self, role_id, **kwargs): for key in kwargs: if key not in ['_fields']: raise InvalidArg(key) kwargs['project_id'] = self.project_id return self.__call(self.knowledgeowl.readerroles(role_id), 'GET', params=urlencode(kwargs))
def test_basic(): """Verify that urlencode works with four levels.""" d = {"a": {"b": {"c": "d"}}} expected = quote("a[b][c]=d", safe="=/&") assert urlencode(d) == expected
def explain(self, endpoint): self.__call(self.knowledgeowl(endpoint), 'GET', params=urlencode({'_method': 'explain'}))
def test_two(): """Verify that urlencode works with two params.""" d = {'a': 'b', 'c': {'d': 'e'}} expected = quote("a=b&c[d]=e", safe="=/&") assert '&'.join(urlencode(d).split('&')) == expected
def test_key_types(): """Verify that urlencode works with key type 'int'.""" d = {1: {2: {3: 4}}} expected = urllib.quote("1[2][3]=4", safe="=/&") assert urlencode(d) == expected
def __request(self, type='GET', content_type='application/json', dry_run=False, params={}, max_401_retry=3, **kwargs): self.type = str.upper(type) if dry_run is True: logging.debug("This is a dry run mode, skipping %s %s" % ( self.type, self.endpoint)) logging.debug("Parameters: %s" % params) return "{'message': 'Dry-run mode, request " "%s %s skipped'}" % ( self.type, self.endpoint) logging.debug('%s %s' % (self.type, self.endpoint)) if params is not None: logging.debug('Input params: %s' % params) request_arguments = {'params': params.copy(), 'verify': False, 'data': {}} if 'metaData' in request_arguments['params']: request_arguments['params']['metaData'] = json.dumps( request_arguments['params']['metaData']) if 'complex' in request_arguments['params']: try: from multidimensional_urlencode import urlencode logging.debug('complex key found, doing dirty stuff...') complex = urlencode({ 'complex': request_arguments['params']['complex']}) self.endpoint += '?%s' % complex del(request_arguments['params']['complex']) except ImportError as e: logging.debug("Unable to handle complex key, missing or" "failed to load multidimensionnal_urlencode: %s" % e) raise ValueError("Unable to handle complex filter") # XXX: To simplify method calling we only support 'params' as keyword. # In case of POST/PUT/DELETE we set params as 'data' keyword of # requests library. In case of file uploading, symbolised by the # presence of 'files' keyword we let params because contacts API does # not support files upload (POST) with parameters in body. if self.type.upper() != 'GET' and 'files' not in kwargs: action = None if 'action' in request_arguments['params']: action = request_arguments['params']['action'] del(request_arguments['params']['action']) request_arguments['data'] = json.dumps( dict( request_arguments['data'], **request_arguments.pop('params') )) request_arguments['headers'] = {'Content-type': content_type} if action is not None: logging.debug("Forcing action parameter to be passed in " "request URL") request_arguments['params'] = {'action': action} arguments = dict(list(request_arguments.items()) + list(kwargs.items())) if self.auth is not None: arguments['auth'] = self.auth logging.debug("Requests args: %s" % arguments) response = getattr(requests, str.lower(self.type))( self.endpoint, **arguments) logging.debug("URL: %s" % response.url) try: # Raise an exception onr 4xx, 5xx HTTPError response.raise_for_status() except Exception as e: def process_http_response(response): if response.status_code == 500: error = '{"status": 500, "message": "Internal Server '\ 'Error"}' else: error = json.loads(response.text) if 'message' in error: details = "" if 'details' in error: details = "(%s)" % error['details'] message = "HTTP code <%s>: %s %s" % (response.status_code, error, details) else: # this is an internal error message = e.message return message # manage 401 if response.status_code == 401: retry = max_401_retry - 1 logging.debug("401 'Unauthorized' status code returned, " "retrying a maximum of %s times" % max_401_retry) while response.status_code == 401 and retry >= 0: if hasattr(self.auth, 'token'): logging.debug("It looks like authentication mecanism " "uses token") self.auth.token.expire() response = getattr(requests, str.lower(self.type))( self.url, **arguments) logging.debug("Retry %s/%s: call to %s returned %s" % ( max_401_retry - retry, max_401_retry, response.url, response.status_code )) retry -= 1 try: response.raise_for_status() except Exception as e: message = process_http_response(response) logging.debug(message) raise Exception(message) else: message = process_http_response(response) raise Exception(message) logging.debug("HTTP code <%s>: OK" % response.status_code) text = response.text or '{}' return json.loads(text)