Last active
September 3, 2018 11:59
-
-
Save satels/a0a0e5612588248f3659e94d75a6bf24 to your computer and use it in GitHub Desktop.
Языки программирования: Python, Ruby. Создание звонка через API calltools.ru и получение результата на PostBack url
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| # Полная и актуальная документация по API: https://calltools.ru/guide_api | |
| CALLTOOLS_PUBLIC_KEY = 'test_public_key' | |
| CALLTOOLS_BASE_URL = 'https://calltools.ru' | |
| CALLTOOLS_TIMEOUT = 30 | |
| class CallToolsException(Exception): | |
| pass | |
| def create_call(campaign_id, phonenumber, text=None, speaker='Tatyana'): | |
| ''' | |
| Создание звонка на прозвон с генерацией ролика | |
| :type campaign_id: int | |
| :type phonenumber: str | |
| :type text: str|None | |
| :type speaker: str | |
| :rtype: dict | |
| ''' | |
| resp = requests.get(CALLTOOLS_BASE_URL + '/lk/cabapi_external/api/v1/phones/call/', { | |
| 'public_key': CALLTOOLS_PUBLIC_KEY, | |
| 'phone': phonenumber, | |
| 'campaign_id': campaign_id, | |
| 'text': text, | |
| 'speaker': speaker, | |
| }, timeout=CALLTOOLS_TIMEOUT) | |
| ret = resp.json() | |
| if ret['status'] == 'error': | |
| raise CallToolsException(ret['data']) | |
| return ret | |
| def check_status(campaign_id, phonenumber=None, call_id=None, | |
| from_created_date=None, to_created_date=None, | |
| from_updated_date=None, to_updated_date=None): | |
| ''' | |
| Получение статуса звонка по номеру телефона или по call_id (ID звонка) | |
| :type campaign_id: int | |
| :type phonenumber: str|None | |
| :type call_id: int|None | |
| :type from_created_date: str|None | |
| :type to_created_date: str|None | |
| :type from_updated_date: str|None | |
| :type to_updated_date: str|None | |
| :rtype: dict | |
| ''' | |
| if phonenumber: | |
| url = '/lk/cabapi_external/api/v1/phones/calls_by_phone/' | |
| elif call_id: | |
| url = '/lk/cabapi_external/api/v1/phones/call_by_id/' | |
| else: | |
| raise ValueError('check_status required call_id or phonenumber') | |
| resp = requests.get(CALLTOOLS_BASE_URL + url, { | |
| 'public_key': CALLTOOLS_PUBLIC_KEY, | |
| 'phone': phonenumber, | |
| 'call_id': call_id, | |
| 'campaign_id': campaign_id, | |
| 'from_created_date': from_created_date, | |
| 'to_created_date': to_created_date, | |
| 'from_updated_date': from_updated_date, | |
| 'to_updated_date': to_updated_date, | |
| }, timeout=CALLTOOLS_TIMEOUT) | |
| ret = resp.json() | |
| if ret['status'] == 'error': | |
| raise CallToolsException(ret['data']) | |
| return ret | |
| def remove_call(campaign_id, phonenumber=None, call_id=None): | |
| ''' | |
| Удаление номера из прозвона | |
| :type campaign_id: int | |
| :type phonenumber: str|None | |
| :type call_id: int|None | |
| :rtype: dict | |
| ''' | |
| if not phonenumber and not call_id: | |
| raise ValueError('remove_call required call_id or phonenumber') | |
| resp = requests.get(CALLTOOLS_BASE_URL + '/lk/cabapi_external/api/v1/phones/remove_call/', { | |
| 'public_key': CALLTOOLS_PUBLIC_KEY, | |
| 'phone': phonenumber, | |
| 'call_id': call_id, | |
| 'campaign_id': campaign_id, | |
| }, timeout=CALLTOOLS_TIMEOUT) | |
| ret = resp.json() | |
| if ret['status'] == 'error': | |
| raise CallToolsException(ret['data']) | |
| return ret | |
| # Статусы звонка | |
| ATTEMPTS_EXCESS_STATUS = 'attempts_exc' | |
| USER_CUSTOM_STATUS = 'user' | |
| NOVALID_BUTTON_STATUS = 'novalid_button' | |
| COMPLETE_FINISHED_STATUS = 'compl_finished' | |
| COMPLETE_NOFINISHED_STATUS = 'compl_nofinished' | |
| DELETED_CALL_STATUS = 'deleted' | |
| IN_PROCESS_STATUS = 'in_process' | |
| HUMAN_STATUSES = [ | |
| (IN_PROCESS_STATUS, 'В процессе'), | |
| (USER_CUSTOM_STATUS, 'Пользовательский IVR'), | |
| (ATTEMPTS_EXCESS_STATUS, 'Попытки закончились'), | |
| (COMPLETE_NOFINISHED_STATUS, 'Некорректный ответ'), | |
| (COMPLETE_FINISHED_STATUS, 'Закончен удачно'), | |
| (NOVALID_BUTTON_STATUS, 'Невалидная кнопка'), | |
| (DELETED_CALL_STATUS, 'Удалён из прозвона'), | |
| ] | |
| # Статусы дозвона | |
| DIAL_STATUS_WAIT = 0 | |
| DIAL_STATUS_FAILED = 1 | |
| DIAL_STATUS_HANGUP = 2 | |
| DIAL_STATUS_RING_TIMEOUT = 3 | |
| DIAL_STATUS_BUSY = 4 | |
| DIAL_STATUS_ANSWER = 5 | |
| DIAL_STATUS_ROBOT1 = 6 | |
| DIAL_STATUS_ROBOT2 = 7 | |
| DIAL_STATUS_NOVALID_BTN = 8 | |
| DIAL_STATUS_UNKNOWN = 9 | |
| DIAL_STATUS_WED = 10 | |
| DIAL_STATUS_USERSTOPLIST = 11 | |
| DIAL_STATUS_GLOBALSTOPLIST = 12 | |
| DIAL_STATUS_WED_WAIT = 13 | |
| DIAL_STATUS_ITSELF_EXC = 14 | |
| DIAL_STATUS_REMOVE = 15 | |
| DIAL_STATUSES = [ | |
| (DIAL_STATUS_WAIT, 'Ожидание вызова (звонка еще нет)'), | |
| (DIAL_STATUS_FAILED, 'Ошибка при вызове абонента'), | |
| (DIAL_STATUS_HANGUP, 'Абонент сбросил звонок'), | |
| (DIAL_STATUS_RING_TIMEOUT, 'Не дозвонились'), | |
| (DIAL_STATUS_BUSY, 'Абонент занят'), | |
| (DIAL_STATUS_ANSWER, 'Абонент ответил'), | |
| (DIAL_STATUS_ROBOT1, 'Ответил автоответчик'), | |
| (DIAL_STATUS_ROBOT2, 'Ответил автоответчик'), | |
| (DIAL_STATUS_NOVALID_BTN, 'Невалидная кнопка'), | |
| (DIAL_STATUS_WED, 'Завершен без действия клиента'), | |
| (DIAL_STATUS_UNKNOWN, 'Неизвестный статус'), | |
| (DIAL_STATUS_USERSTOPLIST, 'Пользовательский стоп-лист'), | |
| (DIAL_STATUS_GLOBALSTOPLIST, 'Глобальный стоп-лист'), | |
| (DIAL_STATUS_WED_WAIT, 'Абонент ответил, но продолжительности разговора не достаточно для фиксации результата в статистике'), | |
| (DIAL_STATUS_ITSELF_EXC, 'Номер абонента совпадает с CallerID'), | |
| (DIAL_STATUS_REMOVE, 'Номер удалён из прозвона'), | |
| ] | |
| # Примеры использование postback в django: | |
| class PostBackForm(forms.Form): | |
| ct_campaign_id = forms.IntegerField() | |
| ct_phone = PhoneNumberField() # See https://github.com/stefanfoulis/django-phonenumber-field | |
| ct_call_id = forms.IntegerField() | |
| ct_completed = forms.DateTimeField(required=False) | |
| ct_status = forms.CharField(required=False) | |
| ct_dial_status = forms.IntegerField() | |
| ct_button_num = forms.IntegerField(required=False) | |
| ct_duration = forms.FloatField(required=False) | |
| def calltools_postback(request): | |
| form = PostBackForm(request.GET or None) | |
| if form.is_valid(): | |
| CallToolsPostBackResult.objects.create(**form.cleaned_data) | |
| return HttpResponse('OK') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'httpclient' | |
| require 'json' | |
| require 'symbolize_keys_recursively' | |
| # CallToolsApi | |
| # | |
| # @author Sergey Blohin | |
| # @email [email protected] | |
| # @see https://calltools.ru/ | |
| module CallToolsApi | |
| # Client | |
| # | |
| # @see https://calltools.ru/guide_api | |
| class Client | |
| # @return [String] CallTools API Version | |
| attr_reader :api_version | |
| # Create CallTools API Client Object | |
| # | |
| # @see https://calltools.ru/lk/users/profile/ | |
| # | |
| # @param [String] api_public_key | |
| def initialize(api_public_key) | |
| @api_version = 'v1'.freeze | |
| @base_url = "https://calltools.ru/lk/cabapi_external/api/#{api_version}".freeze | |
| @api_public_key = api_public_key | |
| end | |
| # Add Call To Campaign | |
| # | |
| # @see https://calltools.ru/lk/phones/all/ | |
| # @see https://calltools.ru/lk/users/profile/ | |
| # | |
| # @param [Integer] campaign_id | |
| # @param [String] phone_number international format +79185274526 | |
| # @return [Hash] :balance, :call_id, :created, :phone | |
| def add_call(campaign_id, phone_number) | |
| url = "#{@base_url}/phones/call/".freeze | |
| post_parameters = { | |
| public_key: @api_public_key, | |
| campaign_id: campaign_id, | |
| phone: phone_number | |
| }.freeze | |
| response = HTTPClient.post url, post_parameters | |
| response_validator response | |
| end | |
| # Add Call With Clip Generation | |
| # | |
| # @see https://calltools.ru/lk/audioclips/all-speakers/ | |
| # @see https://calltools.ru/lk/pages/synth-valid-text/ | |
| # @see https://calltools.ru/lk/phones/all/ | |
| # @see https://calltools.ru/lk/users/profile/ | |
| # | |
| # @param [Integer] campaign_id | |
| # @param [String] phone_number international format +79185274526 | |
| # @param [String] text generated by voice clip, you can also use special markup | |
| # @param [String] speaker voice, if not specified, use default from campaign settings | |
| # | |
| # @return [Hash] :call_id, :phone, :balance, :audioclip_id, :created, :callerid | |
| def add_call_with_clip_generation(campaign_id, phone_number, text, speaker = nil) | |
| url = "#{@base_url}/phones/call/".freeze | |
| post_parameters = { | |
| public_key: @api_public_key, | |
| campaign_id: campaign_id, | |
| phone: phone_number, | |
| text: text, | |
| speaker: speaker | |
| }.freeze | |
| response = HTTPClient.post url, post_parameters | |
| response_validator response | |
| end | |
| # Result by phone number | |
| # | |
| # @see https://calltools.ru/lk/phones/all/ | |
| # @see https://calltools.ru/lk/users/profile/ | |
| # | |
| # @param [Integer] campaign_id | |
| # @param [String] phone_number international format +79185274526 | |
| # @param [Hash] date format is YYYY-MM-DD HH:MM:SS | |
| # @option data [String] from_date_created | |
| # @option data [String] from_date_created | |
| # @option data [String] to_date_created | |
| # @option data [String] from_date_updated | |
| # @option data [String] to_date_updated | |
| # | |
| # @return [Array <Hash>] | |
| def calls_result_by_phone(campaign_id, phone_number, date = {}) | |
| url = "#{@base_url}/phones/calls_by_phone/".freeze | |
| parameters = { | |
| public_key: @api_public_key, | |
| campaign_id: campaign_id, | |
| phone: phone_number, | |
| from_created_date: date[:from_date_created], | |
| to_created_date: date[:to_date_created], | |
| from_updated_date: date[:from_date_updated], | |
| to_updated_date: date[:to_date_updated] | |
| } | |
| response = HTTPClient.get url, parameters | |
| response_validator response | |
| end | |
| # Result By `call_id` | |
| # | |
| # @see #add_call | |
| # @see #add_call_with_clip_generation | |
| # | |
| # @param [Integer] call_id | |
| # | |
| # @return [Array <Hash>] | |
| def calls_result_by_call_id(call_id) | |
| url = "#{@base_url}/phones/call_by_id/".freeze | |
| parameters = { | |
| public_key: @api_public_key, | |
| call_id: call_id | |
| }.freeze | |
| response = HTTPClient.get url, parameters | |
| response_validator response | |
| end | |
| # Remove Call By `phone_number` | |
| # | |
| # @see https://calltools.ru/lk/phones/all/ | |
| # @see https://calltools.ru/lk/users/profile/ | |
| # | |
| # @param [Integer] campaign_id | |
| # @param [String] phone_number international format +79185274526 | |
| # | |
| # @return [Hash] :call_id, :completed, :phone | |
| def remove_call_by_phone_number(campaign_id, phone_number) | |
| url = "#{@base_url}/phones/remove_call/".freeze | |
| parameters = { | |
| public_key: @api_public_key, | |
| campaign_id: campaign_id, | |
| phone: phone_number | |
| }.freeze | |
| response = HTTPClient.post url, parameters | |
| response_validator response | |
| end | |
| # Remove Call By `call_id` | |
| # | |
| # @see https://calltools.ru/lk/phones/all/ | |
| # @see https://calltools.ru/lk/users/profile/ | |
| # @see #add_call | |
| # @see #add_call_with_clip_generation | |
| # | |
| # @param [Integer] campaign_id | |
| # @param [Integer] call_id | |
| # @return [Hash] :call_id, :completed, :phone | |
| def remove_call_by_call_id(campaign_id, call_id) | |
| url = "#{@base_url}/phones/remove_call/".freeze | |
| parameters = { | |
| public_key: @api_public_key, | |
| campaign_id: campaign_id, | |
| call_id: call_id | |
| }.freeze | |
| response = HTTPClient.post url, parameters | |
| response_validator response | |
| end | |
| # Get User Balance | |
| # | |
| # @return [Hash] :balance | |
| def balance | |
| get_url = "#{@base_url}/users/balance/".freeze | |
| get_parameters = { public_key: @api_public_key }.freeze | |
| response = HTTPClient.get get_url, get_parameters | |
| response_validator response | |
| end | |
| # Upload Audiofile | |
| # | |
| # @param [String] clip_name base filename or clip title | |
| # @param [String] clip_filename full path to filename | |
| # @param [String] speaker speaker name | |
| # @param [String] text clip description | |
| # | |
| # @return [Hash] :length, :audioclip_id, :url | |
| def upload_audio(clip_name, clip_filename, speaker = nil, text = nil) | |
| url = "#{@base_url}/audio/upload/".freeze | |
| parameters = { | |
| public_key: @api_public_key, | |
| clip_name: clip_name, | |
| clip_file: File.open(clip_filename), | |
| speaker: speaker, | |
| text: text | |
| }.freeze | |
| response = HTTPClient.post url, parameters | |
| response_validator response | |
| end | |
| # Definition Of The Region And Time Zone (Timezone) by `phone_number` | |
| # | |
| # @see https://calltools.ru/lk/phones/all/ | |
| # | |
| # @param [String] phone_number international format +79185274526 | |
| # | |
| # @return [Hash] | |
| def time_zone_by_phone_number(phone_number) | |
| url = "#{@base_url}/def_codes/by_phone/".freeze | |
| parameters = { phone: phone_number }.freeze | |
| response = HTTPClient.get url, parameters | |
| response_validator response | |
| end | |
| private | |
| # Check http.code is 200 and http.body is JSON | |
| # | |
| # @param [HTTP::Message] response | |
| # | |
| # @return [Hash] http.body as JSON | |
| def response_validator(response) | |
| status = response.status | |
| source_body = response.body | |
| # if http.code is not 200 | |
| # raise code | |
| raise status.to_s unless status == 200 | |
| # if http.body is not JSON | |
| # raise source http.body | |
| begin | |
| body = JSON.parse source_body | |
| # { 'foo' => 42 } => { foo: 42 } | |
| body.symbolize_keys_recursively! | |
| rescue JSON::ParserError | |
| raise source_body | |
| end | |
| end | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment