├── .github └── workflows │ └── unitTests.yml ├── .gitignore ├── AUTHORS.md ├── CHANGELOG.md ├── Dockerfile ├── Jenkinsfile ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── README.md ├── ci └── config.yml ├── docker-compose.yml ├── examples └── JWT.py ├── package.sh ├── plivo ├── __init__.py ├── base.py ├── exceptions.py ├── resources │ ├── __init__.py │ ├── accounts.py │ ├── addresses.py │ ├── applications.py │ ├── brand.py │ ├── call_feedback.py │ ├── calls.py │ ├── campaign.py │ ├── conferences.py │ ├── endpoints.py │ ├── identities.py │ ├── live_calls.py │ ├── lookup.py │ ├── maskingsession.py │ ├── media.py │ ├── messages.py │ ├── multipartycall.py │ ├── nodes.py │ ├── numberpools.py │ ├── numbers.py │ ├── phlos.py │ ├── powerpacks.py │ ├── pricings.py │ ├── profile.py │ ├── queued_calls.py │ ├── recordings.py │ ├── regulatory_compliance.py │ ├── token.py │ ├── tollfree_verification.py │ ├── transcription.py │ ├── verify.py │ └── verify_callerid.py ├── rest │ ├── .DS_Store │ ├── __init__.py │ ├── base_client.py │ ├── client.py │ ├── phlo.py │ └── phlo_client.py ├── utils │ ├── __init__.py │ ├── interactive.py │ ├── jwt.py │ ├── location.py │ ├── signature_v3.py │ ├── template.py │ └── validators.py ├── version.py └── xml │ ├── ConferenceElement.py │ ├── DTMFElement.py │ ├── DialElement.py │ ├── MultiPartyCallElement.py │ ├── PlivoXMLElement.py │ ├── ResponseElement.py │ ├── __init__.py │ ├── breakElement.py │ ├── contElement.py │ ├── emphasisElement.py │ ├── getDigitsElement.py │ ├── getInputElement.py │ ├── hangupElement.py │ ├── langElement.py │ ├── messageElement.py │ ├── numberElement.py │ ├── pElement.py │ ├── phonemeElement.py │ ├── playElement.py │ ├── preAnswerElement.py │ ├── prosodyElement.py │ ├── recordElement.py │ ├── redirectElement.py │ ├── sElement.py │ ├── sayAsElement.py │ ├── speakElement.py │ ├── streamElement.py │ ├── subElement.py │ ├── userElement.py │ ├── wElement.py │ ├── waitElement.py │ └── xmlUtils.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── setup_sdk.sh ├── tests ├── __init__.py ├── base.py ├── decorators.py ├── resources │ ├── __init__.py │ ├── fixtures │ │ ├── accountGetResponse.json │ │ ├── accountUpdateResponse.json │ │ ├── addressCreateResponse.json │ │ ├── addressGetResponse.json │ │ ├── addressListResponse.json │ │ ├── addressUpdateResponse.json │ │ ├── applicationCreateResponse.json │ │ ├── applicationGetResponse.json │ │ ├── applicationListResponse.json │ │ ├── applicationModifyResponse.json │ │ ├── brandCreateResponse.json │ │ ├── brandDeleteResponse.json │ │ ├── brandGetResponse.json │ │ ├── brandGetUsecasesResponse.json │ │ ├── brandListResponse.json │ │ ├── callCreateResponse.json │ │ ├── callGetResponse.json │ │ ├── callListResponse.json │ │ ├── callUpdateResponse.json │ │ ├── campaignCreateResponse.json │ │ ├── campaignDeleteResponse.json │ │ ├── campaignGetNumberResponse.json │ │ ├── campaignGetNumbersResponse.json │ │ ├── campaignGetResponse.json │ │ ├── campaignImportResponse.json │ │ ├── campaignListResponse.json │ │ ├── campaignNumberLinkResponse.json │ │ ├── campaignNumberUnlinkResponse.json │ │ ├── campaignUpdateResponse.json │ │ ├── conferenceDeleteAllResponse.json │ │ ├── conferenceDeleteResponse.json │ │ ├── conferenceGetResponse.json │ │ ├── conferenceListResponse.json │ │ ├── conferenceMemberDeafCreateResponse.json │ │ ├── conferenceMemberDeleteResponse.json │ │ ├── conferenceMemberKickCreateResponse.json │ │ ├── conferenceMemberMuteCreateResponse.json │ │ ├── conferenceMemberPlayCreateResponse.json │ │ ├── conferenceMemberPlayDeleteResponse.json │ │ ├── conferenceMemberSpeakCreateResponse.json │ │ ├── conferenceMemberSpeakDeleteResponse.json │ │ ├── conferenceRecordCreateResponse.json │ │ ├── endpointCreateResponse.json │ │ ├── endpointGetResponse.json │ │ ├── endpointListResponse.json │ │ ├── endpointUpdateResponse.json │ │ ├── identityCreateResponse.json │ │ ├── identityGetResponse.json │ │ ├── identityListResponse.json │ │ ├── identityUpdateResponse.json │ │ ├── liveCallDtmfCreateResponse.json │ │ ├── liveCallGetResponse.json │ │ ├── liveCallListGetResponse.json │ │ ├── liveCallPlayCreateResponse.json │ │ ├── liveCallRecordCreateResponse.json │ │ ├── liveCallSpeakCreateResponse.json │ │ ├── liveCallSpeakDeleteResponse.json │ │ ├── liveCallStreamCreateResponse.json │ │ ├── liveCallStreamDeleteAllResponse.json │ │ ├── liveCallStreamGetAllResponse.json │ │ ├── lookupGetResponse.json │ │ ├── maskingSessionCreateResponse.json │ │ ├── maskingSessionDeleteResponse.json │ │ ├── maskingSessionGetResponse.json │ │ ├── maskingSessionListResponse.json │ │ ├── maskingSessionUpdateResponse.json │ │ ├── mediaGetMediaResponse.json │ │ ├── mediaListResponse.json │ │ ├── messageGetResponse.json │ │ ├── messageListMediaResponse.json │ │ ├── messageListResponse.json │ │ ├── messageSendResponse.json │ │ ├── multiPartyCallsAddParticipantResponse.json │ │ ├── multiPartyCallsEndMpcResponse.json │ │ ├── multiPartyCallsGetParticipantResponse.json │ │ ├── multiPartyCallsKickMpcParticipantResponse.json │ │ ├── multiPartyCallsListMpcResponse.json │ │ ├── multiPartyCallsStartMpcResponse.json │ │ ├── multiPartyCallsStartParticipantRecordingResponse.json │ │ ├── multiPartyCallsStartPlayAudioResponse.json │ │ ├── multiPartyCallsStartRecordingResponse.json │ │ ├── multiPartyCallsUpdateMpcParticipantResponse.json │ │ ├── numberCreateResponse.json │ │ ├── numberGetResponse.json │ │ ├── numberListResponse.json │ │ ├── numberUpdateResponse.json │ │ ├── numberpoolListResponse.json │ │ ├── numberpoolResponse.json │ │ ├── phlosMemberMemberActionsResponse.json │ │ ├── phlosMemberMemberActionsValidationResponse.json │ │ ├── phlosMemberPhloGetResponse.json │ │ ├── phlosMemberPhloGetValidationResponse.json │ │ ├── phoneNumberCreateResponse.json │ │ ├── phoneNumberListResponse.json │ │ ├── powerpackAddNumberResponse.json │ │ ├── powerpackAddTollfreeResponse.json │ │ ├── powerpackBuyAndNumberResponse.json │ │ ├── powerpackCountNumbersResponse.json │ │ ├── powerpackCreatePowerpackResponse.json │ │ ├── powerpackDeletePowerpackResponse.json │ │ ├── powerpackDeleteResponse.json │ │ ├── powerpackFindShortcodeResponse.json │ │ ├── powerpackFindTollfreeResponse.json │ │ ├── powerpackGetPowerpackResponse.json │ │ ├── powerpackListNumbersResponse.json │ │ ├── powerpackListPowerpackResponse.json │ │ ├── powerpackListResponse.json │ │ ├── powerpackListShortcodeResponse.json │ │ ├── powerpackListTollfreeResponse.json │ │ ├── powerpackRemoveNumberResponse.json │ │ ├── powerpackRemoveShortcodeResponse.json │ │ ├── powerpackRemoveTollfreeResponse.json │ │ ├── powerpackResponse.json │ │ ├── powerpackUpdatePowerpackResponse.json │ │ ├── pricingGetResponse.json │ │ ├── profileCreateResponse.json │ │ ├── profileGetResponse.json │ │ ├── profileListResponse.json │ │ ├── profileUpdateResponse.json │ │ ├── recordingGetAddedFilterResponse.json │ │ ├── recordingGetResponse.json │ │ ├── recordingListFromFilterResponse.json │ │ ├── recordingListResponse.json │ │ ├── sessionGetResponse.json │ │ ├── sessionListResponse.json │ │ ├── shortcodeListResponse.json │ │ ├── shortcodeResponse.json │ │ ├── subaccountCreateResponse.json │ │ ├── subaccountGetResponse.json │ │ ├── subaccountListResponse.json │ │ ├── subaccountUpdateResponse.json │ │ ├── tokenCreateResponse.json │ │ ├── tollfreeVerificationCreateResponse.json │ │ ├── tollfreeVerificationGetResponse.json │ │ ├── tollfreeVerificationListResponse.json │ │ ├── tollfreeVerificationUpdateResponse.json │ │ ├── transcriptionCreateResponse.json │ │ ├── transcriptionDeleteResponse.json │ │ ├── transcriptionGetResponse.json │ │ ├── verifyCalleridGetResponse.json │ │ ├── verifyCalleridInitiateResponse.json │ │ ├── verifyCalleridListResponse.json │ │ ├── verifyCalleridUpdateResponse.json │ │ └── verifyCalleridVerifyResponse.json │ ├── test_accounts.py │ ├── test_addresses.py │ ├── test_applications.py │ ├── test_brand.py │ ├── test_calls.py │ ├── test_campaign.py │ ├── test_client.py │ ├── test_conferences.py │ ├── test_endpoints.py │ ├── test_identities.py │ ├── test_jwt.py │ ├── test_lookup.py │ ├── test_maskingsessions.py │ ├── test_medias.py │ ├── test_members.py │ ├── test_messages.py │ ├── test_multipartycalls.py │ ├── test_numbers.py │ ├── test_phlos.py │ ├── test_powerpacks.py │ ├── test_pricings.py │ ├── test_profile.py │ ├── test_recordings.py │ ├── test_signature.py │ ├── test_subaccounts.py │ ├── test_token.py │ ├── test_tollfree_verification.py │ ├── test_transcriptions.py │ ├── test_verify.py │ └── test_verifycallerids.py └── xml │ ├── __init__.py │ ├── test_MultiPartyCallElement.py │ ├── test_breakElement.py │ ├── test_conferenceElement.py │ ├── test_contElement.py │ ├── test_emphasisElement.py │ ├── test_getDigitsElement.py │ ├── test_getInputElement.py │ ├── test_hangupElement.py │ ├── test_langElement.py │ ├── test_messageElement.py │ ├── test_numberElement.py │ ├── test_pElement.py │ ├── test_phonemeElement.py │ ├── test_playElement.py │ ├── test_preAnswerElement.py │ ├── test_prosodyElement.py │ ├── test_recordElement.py │ ├── test_redirectElement.py │ ├── test_responseElement.py │ ├── test_sElement.py │ ├── test_sayAsElement.py │ ├── test_speakElement.py │ ├── test_streamElement.py │ ├── test_subElement.py │ ├── test_userElement.py │ ├── test_wElement.py │ └── test_waitElement.py └── tox.ini /.github/workflows/unitTests.yml: -------------------------------------------------------------------------------- 1 | name: UnitTests 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | name: UnitTests 12 | strategy: 13 | matrix: 14 | python-version: [ 3.8, 3.9, 3.11] 15 | os: [macos-latest] 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | - name: Display Python version 26 | run: python -c "import sys; print(sys.version)" 27 | - name: Dependencies Installation 28 | run: | 29 | pip install tox 30 | - name: Run Tests 31 | run: | 32 | python --version 33 | tox -e py 34 | 35 | coverage: 36 | runs-on: ubuntu-latest 37 | strategy: 38 | matrix: 39 | python-version: [3.11] 40 | steps: 41 | - uses: actions/checkout@v2 42 | - name: Set up Python ${{ matrix.python-version }} 43 | uses: actions/setup-python@v2 44 | with: 45 | python-version: ${{ matrix.python-version }} 46 | - name: Display Python version 47 | run: python -c "import sys; print(sys.version)" 48 | - name: Dependencies Installation 49 | run: | 50 | pip install tox 51 | pip install coverage 52 | - name: Run Tests 53 | run: | 54 | python --version 55 | tox -e py 56 | - name: Upload coverage to Codecov 57 | uses: codecov/codecov-action@v1 58 | with: 59 | flags: unittests 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .coverage 2 | build/ 3 | cover/ 4 | dist/ 5 | *.egg-info/ 6 | .idea/ 7 | *.pyc 8 | *.pyo 9 | ./tests/htmlcov/ 10 | .tox/ 11 | venv*/ 12 | python-sdk-test/ -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Authors 2 | - [Sreyantha Chary](https://sreyanth.com?ref=github/plivo-python) ([@sreyanth](https://github.com/sreyanth)) 3 | - [Aviral Dasgupta](http://www.aviraldg.com) ([@aviraldg](http://github.com/aviraldg)) 4 | - [Abhishek](https://github.com/Abhishek-plivo) 5 | - [Dalibor Dukic](https://github.com/kicdu) 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | WORKDIR /usr/src/app 4 | RUN apk update && apk add git vim bash wget make && apk add --update --no-cache g++ gcc libxslt-dev 5 | 6 | # Copy setup script 7 | COPY setup_sdk.sh /usr/src/app 8 | RUN chmod a+x /usr/src/app/setup_sdk.sh 9 | 10 | ENTRYPOINT [ "/usr/src/app/setup_sdk.sh" ] -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | @Library('plivo_standard_libs@sdks') _ 4 | 5 | sdksPipeline ([ 6 | buildContainer: 'plivo/jenkins-ci/python-sdk:python-sdk-update' 7 | ]) 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018, Plivo Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the license file 2 | include LICENSE.txt 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build test run 2 | 3 | build: 4 | docker-compose up --build --remove-orphans 5 | 6 | start: 7 | docker-compose up --build --remove-orphans --detach 8 | # Wait for the container to be running before attaching 9 | @while [ -z "$$(docker-compose ps -q pythonSDK)" ]; do \ 10 | sleep 1; \ 11 | done 12 | docker attach $$(docker-compose ps -q pythonSDK) 13 | 14 | test: 15 | @[ "${CONTAINER}" ] && \ 16 | (docker exec -it $$CONTAINER /bin/bash -c "tox -e py") || \ 17 | (tox -e py) 18 | 19 | run: 20 | @[ "${CONTAINER}" ] && \ 21 | (docker exec -it $$CONTAINER /bin/bash -c "cd /usr/src/app/python-sdk-test/ && python test.py") || \ 22 | (cd /usr/src/app/python-sdk-test/ && python test.py) -------------------------------------------------------------------------------- /ci/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parent: central 3 | serviceName: plivo-python 4 | language: python-sdk 5 | build: 6 | command: | 7 | ./package.sh 8 | postDeployJobs: 9 | prod: 10 | - name: plivo/messaging-qa/pythonSDKSmoke 11 | disabled: false 12 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | pythonSDK: 6 | build: 7 | context: . 8 | image: pythonsdk 9 | container_name: pythonSDK 10 | environment: 11 | - PLIVO_AUTH_ID=${PLIVO_AUTH_ID} 12 | - PLIVO_AUTH_TOKEN=${PLIVO_AUTH_TOKEN} 13 | - PLIVO_API_DEV_HOST=${PLIVO_API_DEV_HOST} 14 | - PLIVO_API_PROD_HOST=${PLIVO_API_PROD_HOST} 15 | volumes: 16 | - .:/usr/src/app 17 | stdin_open: true 18 | tty: true -------------------------------------------------------------------------------- /examples/JWT.py: -------------------------------------------------------------------------------- 1 | from plivo.utils import jwt 2 | import time 3 | 4 | # using valid_from in epoch and lifetime in seconds 5 | token = jwt.AccessToken('{authId}', '{authToken}', '{endpointUsername}', valid_from=time.time(), lifetime=300, uid='{uid}') 6 | # grants(incoming:bool, outgoing:bool) 7 | token.add_voice_grants(True, True) 8 | print(token.to_jwt()) 9 | 10 | 11 | # using valid_from and valid_till in epoch 12 | token = jwt.AccessToken('{authId}', '{authToken}', '{endpointUsername}', valid_from=time.time(), valid_till=1588751222) 13 | token.add_voice_grants(False, True) 14 | print(token.to_jwt()) 15 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | rm -r dist/ 2 | python setup.py sdist 3 | python setup.py bdist_wheel 4 | -------------------------------------------------------------------------------- /plivo/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from . import exceptions 3 | from .rest import Client as RestClient 4 | from .rest import phlo 5 | from . import xml as plivoxml 6 | -------------------------------------------------------------------------------- /plivo/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | class PlivoRestError(Exception): 3 | pass 4 | 5 | 6 | class AuthenticationError(PlivoRestError): 7 | pass 8 | 9 | 10 | class InvalidRequestError(PlivoRestError): 11 | pass 12 | 13 | 14 | class PlivoServerError(PlivoRestError): 15 | pass 16 | 17 | 18 | class PlivoXMLError(PlivoRestError): 19 | pass 20 | 21 | 22 | class ResourceNotFoundError(PlivoRestError): 23 | pass 24 | 25 | 26 | class ValidationError(PlivoRestError): 27 | pass 28 | 29 | 30 | class ForbiddenError(PlivoRestError): 31 | pass 32 | 33 | class GeoPermissionError(PlivoRestError): 34 | pass 35 | -------------------------------------------------------------------------------- /plivo/resources/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .accounts import Accounts, Subaccounts 3 | from .applications import Applications 4 | from .calls import Calls 5 | from .token import Token 6 | from .conferences import Conferences 7 | from .endpoints import Endpoints 8 | from .messages import Messages 9 | from .numbers import Numbers 10 | from .phlos import Phlos 11 | from .pricings import Pricings 12 | from .recordings import Recordings 13 | from .addresses import Addresses 14 | from .media import Media 15 | from .identities import Identities 16 | from .call_feedback import CallFeedback 17 | from .powerpacks import Powerpacks 18 | from .lookup import Lookup 19 | from .brand import Brand 20 | from .campaign import Campaign 21 | from .profile import Profile 22 | from .multipartycall import MultiPartyCalls, MultiPartyCall, MultiPartyCallParticipant 23 | from .verify import Sessions 24 | from .tollfree_verification import TollfreeVerifications 25 | -------------------------------------------------------------------------------- /plivo/resources/brand.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from plivo.utils.validators import * 3 | 4 | from ..base import ListResponseObject, PlivoResource, PlivoResourceInterface 5 | from ..exceptions import * 6 | from ..utils import * 7 | 8 | class Brand(PlivoResource): 9 | _name = 'Brand' 10 | _identifier_string = 'brand_id' 11 | 12 | class Brand(PlivoResourceInterface): 13 | _resource_type = Brand 14 | 15 | @validate_args(brand_id=[of_type(six.text_type)]) 16 | def get(self, brand_id): 17 | return self.client.request( 18 | 'GET', ('10dlc','Brand', brand_id), response_type=None) 19 | 20 | @validate_args( 21 | type=[optional(of_type(six.text_type))], 22 | status=[optional(of_type(six.text_type))], 23 | limit=[optional(of_type(*six.integer_types))], 24 | offset=[ 25 | optional( 26 | all_of( 27 | of_type(*six.integer_types), 28 | check(lambda offset: 0 <= offset, '0 <= offset'))) 29 | ]) 30 | def list(self, type=None, status=None, 31 | limit=None, offset=None): 32 | return self.client.request( 33 | 'GET', ('10dlc', 'Brand', ), 34 | to_param_dict(self.list, locals()), 35 | response_type=None, 36 | objects_type=None) 37 | 38 | @validate_args( 39 | brand_alias=[required(of_type(six.text_type))], 40 | brand_type=[optional(of_type(six.text_type), is_in(('STANDARD','STARTER')))], 41 | profile_uuid=[required(of_type(six.text_type))], 42 | secondary_vetting=[optional(of_type_exact(bool))], 43 | url=[optional(of_type(six.text_type))], 44 | method=[optional(of_type(six.text_type))]) 45 | def create(self, 46 | brand_alias, 47 | brand_type, 48 | profile_uuid, 49 | secondary_vetting=False, 50 | url='', 51 | method='POST' 52 | ): 53 | return self.client.request('POST', ('10dlc', 'Brand'), 54 | to_param_dict(self.create, locals())) 55 | 56 | 57 | @validate_args(brand_id=[required(of_type(six.text_type))]) 58 | def get_usecases(self, brand_id): 59 | return self.client.request( 60 | 'GET', ('10dlc','Brand', brand_id, 'usecases'), response_type=None) 61 | 62 | @validate_args(brand_id=[required(of_type(six.text_type))]) 63 | def delete(self, brand_id): 64 | return self.client.request( 65 | 'DELETE', ('10dlc','Brand', brand_id), response_type=None) 66 | -------------------------------------------------------------------------------- /plivo/resources/call_feedback.py: -------------------------------------------------------------------------------- 1 | from ..base import PlivoResourceInterface 2 | from requests import Request 3 | from plivo.exceptions import ValidationError 4 | 5 | 6 | FEEDBACK_API_PATH = "v1/Call/{}/Feedback/" 7 | 8 | 9 | class CallFeedback(PlivoResourceInterface): 10 | def create(self, call_uuid, rating, issues=[], notes=""): 11 | if len(call_uuid) == 0: 12 | raise ValidationError('call_uuid cannot be empty') 13 | if not rating: 14 | raise ValidationError('rating cannot be empty') 15 | request_path = FEEDBACK_API_PATH.format(call_uuid) 16 | params_dict = {} 17 | params_dict['rating'] = rating 18 | if len(issues) > 0: 19 | params_dict['issues'] = issues 20 | if len(notes) > 0: 21 | params_dict['notes'] = notes 22 | params_dict['is_callinsights_request'] = True 23 | params_dict['callinsights_request_path'] = request_path 24 | return self.client.request('POST', ('Call', ), params_dict) 25 | -------------------------------------------------------------------------------- /plivo/resources/live_calls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from plivo.base import PlivoResource,\ 3 | PlivoResourceInterface 4 | from plivo.utils import to_param_dict 5 | from ..utils.validators import * 6 | from plivo.utils import to_param_dict 7 | 8 | 9 | class LiveCall(PlivoResource): 10 | _name = 'LiveCall' 11 | _identifier_string = 'call_uuid' 12 | 13 | 14 | class LiveCalls(PlivoResourceInterface): 15 | _resource_type = LiveCall 16 | _iterable = False 17 | 18 | @validate_args( 19 | limit=[ 20 | optional( 21 | all_of( 22 | of_type(*six.integer_types), 23 | check( 24 | lambda limit: 0 < limit <= 20, 25 | message='0 < limit <= 20'))) 26 | ], 27 | offset=[ 28 | optional( 29 | all_of( 30 | of_type(*six.integer_types), 31 | check(lambda offset: 0 <= offset, message='0 <= offset'))) 32 | ], 33 | call_direction=[ 34 | optional(of_type(six.text_type), is_in(('inbound', 'outbound'))) 35 | ], 36 | from_number=[optional(is_phonenumber())], 37 | to_number=[optional(is_iterable(of_type(six.text_type), sep='<'))], 38 | callback_url=[optional(is_url())], 39 | callback_method=[optional(of_type(six.text_type))], 40 | ) 41 | def list_ids(self, 42 | call_direction=None, 43 | from_number=None, 44 | to_number=None, 45 | limit=20, 46 | offset=0, 47 | callback_url=None, 48 | callback_method=None 49 | ): 50 | params = to_param_dict(self.list_ids, locals()) 51 | params.update({'status': 'live'}) 52 | return self.client.request('GET', ('Call',), params, is_voice_request=True) 53 | 54 | @validate_args(_id=[of_type(six.text_type)], 55 | callback_url=[optional(is_url())], 56 | callback_method=[optional(of_type(six.text_type))]) 57 | def get(self, 58 | _id, 59 | callback_url=None, 60 | callback_method=None): 61 | local_object = {} 62 | local_object['status'] = 'live' 63 | if callback_url: 64 | local_object['callback_url'] = callback_url 65 | if callback_method: 66 | local_object['callback_method'] = callback_method 67 | return self.client.request( 68 | 'GET', ('Call', _id), local_object, is_voice_request=True 69 | ) 70 | -------------------------------------------------------------------------------- /plivo/resources/lookup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from plivo.base import PlivoResourceInterface, ResponseObject 3 | 4 | 5 | LOOKUP_API_ENDPOINT = "https://lookup.plivo.com/v1/Number" 6 | 7 | 8 | class Number(ResponseObject): 9 | def __init__(self, client, data): 10 | super(Number, self).__init__(data) 11 | 12 | 13 | class Lookup(PlivoResourceInterface): 14 | _resource_type = Number 15 | 16 | def get(self, number, info_type='carrier'): 17 | params = { 18 | 'type': info_type, 19 | } 20 | return self.client.request( 21 | 'GET', 22 | (LOOKUP_API_ENDPOINT, number), 23 | data=params, 24 | response_type=Number, 25 | is_lookup_request=True) 26 | -------------------------------------------------------------------------------- /plivo/resources/media.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from plivo.utils.validators import * 3 | 4 | from ..base import ListResponseObject, PlivoResource, PlivoResourceInterface 5 | from ..exceptions import * 6 | from ..utils import * 7 | 8 | 9 | class Media(PlivoResource): 10 | _name = 'Media' 11 | _identifier_string = 'media_id' 12 | 13 | def get(self): 14 | return self.client.medias.get(self.id) 15 | 16 | 17 | class Media(PlivoResourceInterface): 18 | _resource_type = Media 19 | 20 | @validate_args( 21 | media_file=[optional(of_type_exact(list))]) 22 | def upload(self, media_file): 23 | if media_file: 24 | fileList = [] 25 | for media_url in media_file: 26 | file_extension = media_url.strip().split('.')[-1].lower() 27 | if file_extension not in ['jpeg', 'jpg', 'png', 'xcf', 'plain', 'pdf', 'mpeg', 'mp4']: 28 | raise ValidationError( 29 | 'File format of the file to be uploaded should be one of JPG, JPEG, PNG or PDF' 30 | ) 31 | content_types = { 32 | 'jpeg': 'image/jpeg', 33 | 'jpg': 'image/jpeg', 34 | 'png': 'image/png', 35 | 'pdf': 'application/pdf', 36 | 'xcf': 'image/xcf', 37 | 'text': 'text/plain', 38 | 'mpeg': 'video/mpeg', 39 | 'mp4': 'video/mp4' 40 | } 41 | print(media_url) 42 | import os 43 | files = ( 44 | 'file', (media_url.split(os.sep)[-1], open( 45 | media_url, 'rb'), content_types[file_extension]) 46 | ) 47 | fileList.append(files) 48 | data_to_send = {} 49 | return self.client.request( 50 | 'POST', ('Media', ), data_to_send, files=fileList) 51 | 52 | @validate_args(media_id=[of_type(six.text_type)]) 53 | def get(self, media_id): 54 | return self.client.request( 55 | 'GET', ('Media', media_id), response_type=None) 56 | 57 | @validate_args( 58 | limit=[ 59 | optional( 60 | all_of( 61 | of_type(*six.integer_types), 62 | check(lambda limit: 0 < limit <= 20, '0 < limit <= 20'))) 63 | ], 64 | offset=[ 65 | optional( 66 | all_of( 67 | of_type(*six.integer_types), 68 | check(lambda offset: 0 <= offset, '0 <= offset'))) 69 | ]) 70 | def list(self, 71 | limit=20, 72 | offset=0): 73 | return self.client.request( 74 | 'GET', ('Media', ), 75 | to_param_dict(self.list, locals()), 76 | response_type=None) 77 | -------------------------------------------------------------------------------- /plivo/resources/nodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Node class 4 | """ 5 | from plivo.base import PlivoResource 6 | from plivo.utils import to_param_dict 7 | from plivo.utils.validators import * 8 | 9 | 10 | class Node(PlivoResource): 11 | _name = 'Node' 12 | _identifier_string = 'node_id' 13 | 14 | @validate_args( 15 | action=[of_type(six.text_type)]) 16 | def update(self, 17 | action, 18 | trigger_source, 19 | to, 20 | role): 21 | return self.client.request('POST', ('phlo', self.phlo_id, self.node_type, self.node_id), 22 | to_param_dict(self.update, locals())) 23 | 24 | 25 | class MultiPartyCall(Node): 26 | _name = 'MultiPartyCall' 27 | _identifier_string = 'node_id' 28 | 29 | def call(self, 30 | trigger_source, 31 | to, 32 | role): 33 | return self.update('call', trigger_source, to, role) 34 | 35 | def warm_transfer(self, 36 | trigger_source, 37 | to, 38 | role='agent'): 39 | return self.update('warm_transfer', trigger_source, to, role) 40 | 41 | def cold_transfer(self, 42 | trigger_source, 43 | to, 44 | role='agent'): 45 | return self.update('cold_transfer', trigger_source, to, role) 46 | 47 | def member(self, member_id): 48 | self.member_id = member_id 49 | data = { 50 | 'member_id': member_id, 51 | 'phlo_id': self.phlo_id, 52 | 'node_id': self.node_id, 53 | 'node_type': self.node_type 54 | } 55 | return Member(self.client, data) 56 | 57 | 58 | class Member(PlivoResource): 59 | _name = 'Member' 60 | _identifier_string = 'member_id' 61 | 62 | def abort_transfer(self): 63 | return self.update('abort_transfer') 64 | 65 | def resume_call(self): 66 | return self.update('resume_call') 67 | 68 | def voicemail_drop(self): 69 | return self.update('voicemail_drop') 70 | 71 | def hangup(self): 72 | return self.update('hangup') 73 | 74 | def hold(self): 75 | return self.update('hold') 76 | 77 | def unhold(self): 78 | return self.update('unhold') 79 | 80 | def mute(self): 81 | return self.update('mute') 82 | 83 | def unmute(self): 84 | return self.update('unmute') 85 | 86 | def update(self, 87 | action): 88 | return self.client.request('POST', ('phlo', self.phlo_id, self.node_type, self.node_id, 'members', self.member_id), 89 | to_param_dict(self.update, locals())) 90 | -------------------------------------------------------------------------------- /plivo/resources/pricings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Pricing class - along with its list class 4 | """ 5 | 6 | from plivo.base import PlivoResource, PlivoResourceInterface 7 | from plivo.utils import to_param_dict 8 | from plivo.utils.validators import * 9 | 10 | 11 | class Pricing(PlivoResource): 12 | _name = 'Pricing' 13 | _identifier_string = 'country_iso' 14 | 15 | def get(self): 16 | return self.client.pricing.get(self.id) 17 | 18 | 19 | class Pricings(PlivoResourceInterface): 20 | _resource_type = Pricing 21 | 22 | @validate_args(country_iso=[regex(r'^[A-Z]{2}$')]) 23 | def get(self, country_iso): 24 | return self.client.request( 25 | 'GET', ('Pricing', ), 26 | to_param_dict(self.get, locals()), 27 | response_type=Pricing) 28 | -------------------------------------------------------------------------------- /plivo/resources/queued_calls.py: -------------------------------------------------------------------------------- 1 | from plivo.base import PlivoResource, PlivoResourceInterface 2 | 3 | from ..utils.validators import * 4 | 5 | 6 | class QueuedCall(PlivoResource): 7 | _name = 'QueuedCall' 8 | _identifier_string = 'call_uuid' 9 | 10 | 11 | class QueuedCalls(PlivoResourceInterface): 12 | _resource_type = QueuedCall 13 | _iterable = False 14 | 15 | @validate_args( 16 | limit=[ 17 | optional( 18 | all_of( 19 | of_type(*six.integer_types), 20 | check( 21 | lambda limit: 0 < limit <= 20, 22 | message='0 < limit <= 20'))) 23 | ], 24 | offset=[ 25 | optional( 26 | all_of( 27 | of_type(*six.integer_types), 28 | check(lambda offset: 0 <= offset, message='0 <= offset'))) 29 | ], 30 | callback_url=[optional(is_url())], 31 | callback_method=[optional(of_type(six.text_type))],) 32 | def list_ids(self, limit=20, offset=0, callback_url=None, callback_method=None): 33 | return self.client.request('GET', ('Call', ), { 34 | 'status': 'queued', 35 | 'limit': limit, 36 | 'offset': offset, 37 | 'callback_url': callback_url, 38 | 'callback_method': callback_method 39 | }, is_voice_request=True) 40 | 41 | @validate_args(_id=[of_type(six.text_type)], 42 | callback_url=[optional(is_url())], 43 | callback_method=[optional(of_type(six.text_type))],) 44 | def get(self, 45 | _id, 46 | callback_url=None, 47 | callback_method=None): 48 | local_object={} 49 | local_object['status'] = 'queued' 50 | if callback_url: 51 | local_object['callback_url'] = callback_url 52 | if callback_method: 53 | local_object['callback_method'] = callback_method 54 | 55 | return self.client.request('GET', ('Call', _id), local_object, is_voice_request=True) 56 | -------------------------------------------------------------------------------- /plivo/resources/token.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | from plivo.base import (PlivoResourceInterface) 4 | from plivo.utils.validators import * 5 | 6 | 7 | class Token(PlivoResourceInterface): 8 | @validate_args( 9 | iss=[required(of_type(six.text_type))], 10 | sub=[optional(of_type(six.text_type))], 11 | nbf=[optional(of_type(six.text_type))], 12 | exp=[optional(of_type(six.text_type))], 13 | incoming_allow=[optional(of_type(bool, string))], 14 | outgoing_allow=[optional(of_type(bool, string))], 15 | app=[optional(of_type(six.text_type))] 16 | ) 17 | def create(self, iss, sub=None, nbf=None, exp=None, incoming_allow=None, outgoing_allow=None, app=None): 18 | if incoming_allow is True and sub is None: 19 | raise ValueError('sub is required when incoming_allow is true') 20 | else: 21 | params = {'iss': iss} 22 | 23 | if sub: 24 | params['sub'] = sub 25 | if nbf: 26 | params['nbf'] = nbf 27 | if exp: 28 | params['exp'] = exp 29 | if incoming_allow or outgoing_allow: 30 | params['per'] = {} 31 | params['per']['voice'] = {} 32 | if incoming_allow: 33 | params['per']['voice']['incoming_allow'] = incoming_allow 34 | if outgoing_allow: 35 | params['per']['voice']['outgoing_allow'] = outgoing_allow 36 | if app: 37 | params['app'] = app 38 | 39 | return self.client.request('POST', ('JWT', 'Token',), params, is_voice_request=True) 40 | -------------------------------------------------------------------------------- /plivo/resources/transcription.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Recording class - along with its list class 4 | """ 5 | 6 | from plivo.base import (ListResponseObject, PlivoResource, 7 | PlivoResourceInterface) 8 | from plivo.resources.accounts import Subaccount 9 | from plivo.utils import is_valid_time_comparison, to_param_dict 10 | from plivo.utils.validators import * 11 | 12 | 13 | class Transcription(PlivoResource): 14 | _name = 'Transcription' 15 | _identifier_string = 'transcription_id' 16 | 17 | def get_transcription(self): 18 | return self.client.transcriptions.get_transcription(self.id, **to_param_dict(self.get_transcription(), locals())) 19 | 20 | def create_transcription(self): 21 | return self.client.transcriptions.create_transcription(self.id, **to_param_dict(self.create_transcription(), locals())) 22 | 23 | def delete_transcription(self): 24 | return self.client.transcriptions.delete_transcription(self.id, **to_param_dict(self.delete_transcription(), locals())) 25 | 26 | 27 | class Transcriptions(PlivoResourceInterface): 28 | _resource_type = Transcription 29 | 30 | @validate_args(transcription_id=[of_type(six.text_type)] 31 | ) 32 | def get_transcription(self, transcription_id, type=None): 33 | if not type: 34 | return self.client.request( 35 | 'GET', ('Transcription', transcription_id), is_voice_request=True) 36 | else: 37 | return self.client.request( 38 | 'GET', ('Transcription', transcription_id), to_param_dict(self.get_transcription, locals()), is_voice_request=True) 39 | 40 | def create_transcription(self, recording_id, transcription_callback_url=None): 41 | return self.client.request( 42 | 'POST', ('Transcription', recording_id), to_param_dict(self.create_transcription, locals()), is_voice_request=True) 43 | 44 | def delete_transcription(self, transcription_id): 45 | return self.client.request( 46 | 'DELETE', ('Transcription', transcription_id), is_voice_request=True) 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /plivo/rest/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plivo/plivo-python/4f29e49de8acd7fb4ec547cb2a163ed667ace122/plivo/rest/.DS_Store -------------------------------------------------------------------------------- /plivo/rest/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .client import Client 3 | -------------------------------------------------------------------------------- /plivo/rest/phlo.py: -------------------------------------------------------------------------------- 1 | from .phlo_client import PhloClient as RestClient -------------------------------------------------------------------------------- /plivo/rest/phlo_client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Phlo client, used for Phlo API requests. 4 | """ 5 | from plivo.resources import Phlos 6 | from plivo.rest.base_client import BaseClient 7 | from requests import Request 8 | 9 | PHLO_API = 'https://phlorunner.plivo.com' 10 | PHLO_API_BASE_URI = '/'.join([PHLO_API, 'v1']) 11 | 12 | 13 | class PhloClient(BaseClient): 14 | def __init__(self, auth_id=None, auth_token=None, proxies=None, timeout=5): 15 | """ 16 | The Plivo API client. 17 | 18 | Deals with all the API requests to be made. 19 | """ 20 | BaseClient.__init__(self, auth_id, auth_token, proxies, timeout) 21 | 22 | self.phlo_base_uri = PHLO_API_BASE_URI 23 | self.phlo = Phlos(self) 24 | 25 | def create_request(self, method, path=None, data=None): 26 | path = path or [] 27 | 28 | req = Request(method, '/'.join([self.phlo_base_uri] + 29 | list([str(p) for p in path])) + '/', 30 | **({ 31 | 'params': data 32 | } if method == 'GET' else { 33 | 'json': data 34 | })) 35 | return self.session.prepare_request(req) -------------------------------------------------------------------------------- /plivo/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import inspect 3 | import re 4 | from datetime import datetime 5 | 6 | from hmac import new as hnew 7 | from hashlib import sha256 8 | from .signature_v3 import validate_v3_signature 9 | 10 | try: 11 | from urllib.parse import urlparse, urlunparse 12 | except ImportError: 13 | from urlparse import urlparse, urlunparse 14 | 15 | try: 16 | from base64 import encodebytes as base64_encode 17 | except ImportError: 18 | from base64 import encodestring as base64_encode 19 | 20 | try: 21 | from inspect import getfullargspec as getargspec 22 | except ImportError: 23 | from inspect import getargspec as getargspec 24 | 25 | def validate_signature(uri, nonce, signature, auth_token=''): 26 | """ 27 | Validates requests made by Plivo to your servers. 28 | 29 | :param uri: Your server URL 30 | :param nonce: X-Plivo-Signature-V2-Nonce 31 | :param signature: X-Plivo-Signature-V2 header 32 | :param auth_token: Plivo Auth token 33 | :return: True if the request matches signature, False otherwise 34 | """ 35 | 36 | auth_token = bytes(auth_token.encode('utf-8')) 37 | nonce = bytes(nonce.encode('utf-8')) 38 | signature = bytes(signature.encode('utf-8')) 39 | 40 | parsed_uri = urlparse(uri.encode('utf-8')) 41 | base_url = urlunparse((parsed_uri.scheme.decode('utf-8'), 42 | parsed_uri.netloc.decode('utf-8'), 43 | parsed_uri.path.decode('utf-8'), '', '', 44 | '')).encode('utf-8') 45 | 46 | return base64_encode(hnew(auth_token, base_url + nonce, sha256) 47 | .digest()).strip() == signature 48 | 49 | 50 | def is_valid_time_comparison(time): 51 | if isinstance(time, datetime): 52 | return True 53 | return False 54 | 55 | 56 | def is_valid_subaccount(subaccount): 57 | subaccount_string = str(subaccount) 58 | if len(subaccount_string) == 20 and subaccount_string[:2] == 'SA': 59 | return True 60 | return False 61 | 62 | 63 | def is_valid_mainaccount(mainaccount): 64 | mainaccount_string = str(mainaccount) 65 | if len(mainaccount_string) == 20 and mainaccount_string[:2] == 'MA': 66 | return True 67 | return False 68 | 69 | 70 | def to_param_dict(func, vals, exclude_none=True, func_args_check=True): 71 | args = getargspec(func)[0] 72 | arg_names = list(args) 73 | # The bit of regex magic below is for arguments that are keywords in 74 | # Python, like from. These can't be used directly, so our convention is to 75 | # add "_" suffixes to them. This strips them out. 76 | pd = { 77 | re.sub(r'^(.*)_+$', r'\1', key): value 78 | for key, value in vals.items() 79 | if key != 'self' and (key in arg_names or func_args_check==False) and ( 80 | value is not None or exclude_none is False) 81 | } 82 | return pd 83 | -------------------------------------------------------------------------------- /plivo/utils/interactive.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import validate_args, optional, of_type_exact, validate_list_items, validate_dict_items 2 | 3 | class Header: 4 | @validate_args( 5 | type=[optional(of_type_exact(str, type(None)))], 6 | text=[optional(of_type_exact(str, type(None)))], 7 | media=[optional(of_type_exact(str, type(None)))] 8 | ) 9 | def __init__(self, type=None, text=None, media=None): 10 | self.type = type 11 | self.text = text 12 | self.media = media 13 | 14 | class Body: 15 | @validate_args( 16 | text=[optional(of_type_exact(str, type(None)))] 17 | ) 18 | def __init__(self, text=None): 19 | self.text = text 20 | 21 | class Footer: 22 | @validate_args( 23 | text=[optional(of_type_exact(str, type(None)))] 24 | ) 25 | def __init__(self, text=None): 26 | self.text = text 27 | 28 | class Row: 29 | @validate_args( 30 | id=[optional(of_type_exact(str, type(None)))], 31 | title=[optional(of_type_exact(str, type(None)))], 32 | description=[optional(of_type_exact(str, type(None)))] 33 | ) 34 | def __init__(self, id=None, title=None, description=None): 35 | self.id = id 36 | self.title = title 37 | self.description = description 38 | 39 | class Section: 40 | @validate_args( 41 | title=[optional(of_type_exact(str, type(None)))], 42 | rows=[optional(validate_list_items(Row))] 43 | ) 44 | def __init__(self, title=None, rows=None): 45 | self.title = title 46 | self.rows = rows if rows is not None else [] 47 | 48 | class Btn: 49 | @validate_args( 50 | id=[optional(of_type_exact(str, type(None)))], 51 | title=[optional(of_type_exact(str, type(None)))], 52 | cta_url=[optional(of_type_exact(str, type(None)))] 53 | ) 54 | def __init__(self, id=None, title=None, cta_url=None): 55 | self.id = id 56 | self.title = title 57 | self.cta_url = cta_url 58 | 59 | class Action: 60 | @validate_args( 61 | buttons=[optional(validate_list_items(Btn))], 62 | sections=[optional(validate_list_items(Section))] 63 | ) 64 | def __init__(self, buttons=None, sections=None): 65 | self.buttons = buttons if buttons is not None else [] 66 | self.sections = sections if sections is not None else [] 67 | 68 | class Interactive: 69 | @validate_args( 70 | type=[optional(of_type_exact(str, type(None)))], 71 | header=[optional(validate_dict_items(Header))], 72 | body=[optional(validate_dict_items(Body))], 73 | footer=[optional(validate_dict_items(Footer))], 74 | action=[optional(validate_dict_items(Action))] 75 | ) 76 | def __init__(self, type=None, header=None, body=None, footer=None, action=None): 77 | self.type = type 78 | # Assign directly the validated dictionary or default to None if not provided 79 | self.header = header 80 | self.body = body 81 | self.footer = footer 82 | self.action = action 83 | -------------------------------------------------------------------------------- /plivo/utils/jwt.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | import jwt, time 3 | from plivo.utils.validators import * 4 | 5 | """ 6 | Class to represent plivo token for endpoint authentication 7 | """ 8 | class AccessToken: 9 | auth_id = '' 10 | username = '' 11 | valid_from = 0 12 | lifetime = 86400 13 | key = '' 14 | grants = {} 15 | uid = 0 16 | 17 | @validate_args( 18 | auth_id=[is_account_id()], 19 | auth_token=[optional(of_type(six.text_type))], 20 | username=[all_of( 21 | of_type(six.text_type), 22 | check(lambda username: len(username) > 0, 'empty username') 23 | )], 24 | valid_from=[optional(of_type(*six.integer_types))], 25 | lifetime=[ 26 | optional( 27 | all_of( 28 | of_type(*six.integer_types), 29 | check(lambda lifetime: 180 <= lifetime <= 86400, 30 | '180 < lifetime <= 86400'))) 31 | ], 32 | valid_till=[optional(of_type(*six.integer_types))], 33 | ) 34 | def __init__(self, 35 | auth_id, 36 | auth_token, 37 | username, 38 | valid_from=None, 39 | lifetime=None, 40 | valid_till=None, 41 | uid=None): 42 | self.auth_id = auth_id 43 | self.username = username 44 | if valid_from: 45 | self.valid_from = int(valid_from) 46 | else: 47 | self.valid_from = int(time.time()) 48 | if lifetime: 49 | self.lifetime = int(lifetime) 50 | if valid_till is not None: 51 | raise ValidationError("use either lifetime or valid_till") 52 | elif valid_till: 53 | self.lifetime = valid_till - self.valid_from 54 | if self.lifetime < 0: 55 | raise ValidationError( 56 | "validity expires %s seconds before it starts" % 57 | self.lifetime) 58 | if self.lifetime < 180 or self.lifetime > 86400: 59 | raise ValidationError( 60 | "validity of %s seconds is out of permitted range [180, 86400]" % 61 | self.lifetime) 62 | 63 | self.key = auth_token 64 | 65 | if uid: 66 | self.uid = uid 67 | else: 68 | self.uid = "%s-%s" % (username, time.time()) 69 | 70 | @validate_args( 71 | incoming=[optional(of_type_exact(bool))], 72 | outgoing=[optional(of_type_exact(bool))], 73 | ) 74 | def add_voice_grants(self, incoming=False, outgoing=False): 75 | self.grants['voice'] = { 76 | 'incoming_allow': incoming, 77 | 'outgoing_allow': outgoing 78 | } 79 | 80 | def to_jwt(self): 81 | headers = {'typ': 'JWT', 'cty': 'plivo;v=1'} 82 | algorithm = 'HS256' 83 | claims = { 84 | 'jti': self.uid, 85 | 'iss': self.auth_id, 86 | 'sub': self.username, 87 | 'nbf': self.valid_from, 88 | 'exp': self.valid_from + self.lifetime, 89 | 'grants': self.grants 90 | } 91 | return jwt.encode(claims, self.key, algorithm, headers).decode('utf-8') 92 | -------------------------------------------------------------------------------- /plivo/utils/location.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import validate_args, required, of_type_exact 2 | 3 | class Location: 4 | @validate_args( 5 | latitude=[required(of_type_exact(str))], 6 | longitude=[required(of_type_exact(str))], 7 | name=[required(of_type_exact(str))], 8 | address=[required(of_type_exact(str))] 9 | ) 10 | def __init__(self, latitude, longitude, name, address): 11 | self.latitude = latitude 12 | self.longitude = longitude 13 | self.name = name 14 | self.address = address 15 | -------------------------------------------------------------------------------- /plivo/utils/template.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import * 2 | from plivo.utils.location import * 3 | 4 | class Parameter: 5 | @validate_args( 6 | type=[required(of_type_exact(str))], 7 | text=[optional(of_type_exact(str, type(None)))], 8 | media=[optional(of_type_exact(str))], 9 | payload=[optional(of_type_exact(str))], 10 | currency=[optional(of_type_exact(dict))], 11 | date_time=[optional(of_type_exact(dict))], 12 | location=[optional(validate_dict_items(Location))], 13 | parameter_name=[optional(of_type_exact(str))], 14 | ) 15 | def __init__(self, type, text=None, media=None, payload=None, currency=None, date_time=None, location=None, parameter_name=None): 16 | self.type = type 17 | self.text = text 18 | self.media = media 19 | self.payload = payload 20 | self.currency = Currency(**currency) if currency else None 21 | self.date_time = DateTime(**date_time) if date_time else None 22 | self.location = location 23 | self.parameter_name = parameter_name 24 | 25 | class Component: 26 | @validate_args( 27 | type=[required(of_type_exact(str))], 28 | sub_type=[optional(of_type_exact(str, type(None)))], 29 | index=[optional(of_type_exact(str, type(None)))], 30 | parameters=[optional(validate_list_items(Parameter))], 31 | ) 32 | def __init__(self, type, sub_type=None, index=None, parameters=None): 33 | self.type = type 34 | self.sub_type = sub_type 35 | self.index = index 36 | self.parameters = parameters if parameters is not None else [] 37 | 38 | class Template: 39 | @validate_args( 40 | name=[required(of_type_exact(str))], 41 | language=[required(of_type_exact(str))], 42 | components=[optional(validate_list_items(Component))], 43 | ) 44 | def __init__(self, name, language, components=None): 45 | self.name = name 46 | self.language = language 47 | self.components = components if components is not None else [] 48 | 49 | 50 | class Currency: 51 | @validate_args( 52 | fallback_value=[required(of_type_exact(str))], 53 | currency_code=[required(of_type_exact(str))], 54 | amount_1000=[required(of_type_exact(int))], 55 | ) 56 | def __init__(self, fallback_value, currency_code, amount_1000): 57 | self.fallback_value=fallback_value 58 | self.currency_code = currency_code 59 | self.amount_1000 = amount_1000 60 | 61 | 62 | class DateTime: 63 | @validate_args( 64 | fallback_value=[required(of_type_exact(str))], 65 | ) 66 | def __init__(self, fallback_value): 67 | self.fallback_value = fallback_value 68 | -------------------------------------------------------------------------------- /plivo/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = '4.59.1' 3 | -------------------------------------------------------------------------------- /plivo/xml/DTMFElement.py: -------------------------------------------------------------------------------- 1 | from plivo.xml import PlivoXMLElement, map_type 2 | from plivo.utils.validators import * 3 | 4 | 5 | class DTMFElement(PlivoXMLElement): 6 | _name = 'DTMF' 7 | _nestable = [] 8 | 9 | @property 10 | def async_(self): 11 | return self.__async 12 | 13 | @async_.setter 14 | def async_(self, value): 15 | self.__async = bool(value) if value is not None else None 16 | 17 | @validate_args( 18 | value=[of_type_exact(bool)], 19 | ) 20 | def set_async(self, value): 21 | self.async_ = value 22 | return self 23 | 24 | def __init__( 25 | self, 26 | content, 27 | async_=None, 28 | ): 29 | super(DTMFElement, self).__init__() 30 | 31 | self.content = content 32 | self.async_ = async_ 33 | 34 | def to_dict(self): 35 | d = { 36 | 'async': self.async_, 37 | } 38 | return { 39 | k: six.text_type(map_type(v)) 40 | for k, v in d.items() if v is not None 41 | } 42 | -------------------------------------------------------------------------------- /plivo/xml/PlivoXMLElement.py: -------------------------------------------------------------------------------- 1 | from lxml import etree 2 | import six 3 | from plivo.exceptions import PlivoXMLError 4 | 5 | 6 | class PlivoXMLElement(object): 7 | def __init__(self): 8 | self.content = '' 9 | self.children = [] 10 | 11 | def add(self, element): 12 | if not isinstance(element, PlivoXMLElement): 13 | raise PlivoXMLError('element must be a PlivoXMLElement') 14 | 15 | if element._name not in self._nestable: 16 | raise PlivoXMLError( 17 | '{} is not nestable in {} (allowed: {})'.format( 18 | element._name, self._name, self._nestable)) 19 | self.children.append(element) 20 | return self 21 | 22 | def continue_speak(self, body=None): 23 | return body.replace('', ' ').replace('', ' ') 24 | 25 | def to_string(self, pretty=True): 26 | s = self.continue_speak(etree.tostring(self._to_element(), pretty_print=pretty, encoding='unicode')) 27 | 28 | if not isinstance(s, str): 29 | s = s.encode('utf-8') 30 | return s 31 | 32 | def _to_element(self, parent=None): 33 | e = etree.SubElement( 34 | parent, self._name, 35 | **self.to_dict()) if parent is not None else etree.Element( 36 | self._name, **self.to_dict()) 37 | if self.content: 38 | try: 39 | if six.PY2 and isinstance(self.content, str): 40 | e.text = self.content.decode() 41 | elif six.PY3 and isinstance(self.content, bytes): 42 | e.text = self.content.decode() 43 | else: 44 | e.text = self.content 45 | except: 46 | e.text = self.content 47 | for child in self.children: 48 | child._to_element(parent=e) 49 | return e 50 | -------------------------------------------------------------------------------- /plivo/xml/__init__.py: -------------------------------------------------------------------------------- 1 | from .xmlUtils import map_type 2 | from .PlivoXMLElement import PlivoXMLElement 3 | from .ConferenceElement import ConferenceElement 4 | from .DTMFElement import DTMFElement 5 | from .MultiPartyCallElement import MultiPartyCallElement 6 | from .hangupElement import HangupElement 7 | from .messageElement import MessageElement 8 | from .numberElement import NumberElement 9 | from .playElement import PlayElement 10 | from .waitElement import WaitElement 11 | from .recordElement import RecordElement 12 | from .redirectElement import RedirectElement 13 | from .userElement import UserElement 14 | from .breakElement import BreakElement 15 | from .langElement import LangElement 16 | from .emphasisElement import EmphasisElement 17 | from .speakElement import SpeakElement 18 | from .getDigitsElement import GetDigitsElement 19 | from .getInputElement import GetInputElement 20 | from .preAnswerElement import PreAnswerElement 21 | from .DialElement import DialElement 22 | from .ResponseElement import ResponseElement 23 | from .pElement import PElement 24 | from .phonemeElement import PhonemeElement 25 | from .prosodyElement import ProsodyElement 26 | from .sElement import SElement 27 | from .sayAsElement import SayAsElement 28 | from .subElement import SubElement 29 | from .wElement import WElement 30 | from .contElement import ContElement 31 | from .streamElement import StreamElement -------------------------------------------------------------------------------- /plivo/xml/breakElement.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import * 2 | from plivo.xml import PlivoXMLElement, map_type 3 | 4 | 5 | class BreakElement(PlivoXMLElement): 6 | _name = 'break' 7 | _nestable = [] 8 | 9 | @property 10 | def strength(self): 11 | return self.__strength 12 | 13 | @strength.setter 14 | def strength(self, value): 15 | self.__strength = six.text_type( 16 | value) if value is not None else None 17 | 18 | @validate_args( 19 | value=[of_type(six.text_type)], 20 | ) 21 | def set_strength(self, value): 22 | self.strength = value 23 | return self 24 | 25 | @property 26 | def time(self): 27 | return self.__time 28 | 29 | @time.setter 30 | def time(self, value): 31 | self.__time = six.text_type( 32 | value) if value is not None else None 33 | 34 | @validate_args( 35 | value=[of_type(six.text_type)], 36 | ) 37 | def set_time(self, value): 38 | self.time = value 39 | return self 40 | 41 | def __init__( 42 | self, 43 | content=None, 44 | strength=None, 45 | time=None, 46 | ): 47 | 48 | super(BreakElement, self).__init__() 49 | self.content = content 50 | self.strength = strength 51 | self.time = time 52 | 53 | def to_dict(self): 54 | d = { 55 | 'strength': self.strength, 56 | 'time': self.time, 57 | } 58 | 59 | return { 60 | k: six.text_type(map_type(v)) 61 | for k, v in d.items() if v is not None 62 | } 63 | -------------------------------------------------------------------------------- /plivo/xml/hangupElement.py: -------------------------------------------------------------------------------- 1 | from plivo.xml import PlivoXMLElement, map_type 2 | from plivo.utils.validators import * 3 | 4 | 5 | class HangupElement(PlivoXMLElement): 6 | _name = 'Hangup' 7 | _nestable = [] 8 | 9 | @property 10 | def reason(self): 11 | return self.__reason 12 | 13 | @reason.setter 14 | def reason(self, value): 15 | self.__reason = six.text_type(value) if value is not None else None 16 | 17 | @validate_args( 18 | value=[of_type(six.text_type)], 19 | ) 20 | def set_reason(self, value): 21 | self.reason = value 22 | return self 23 | 24 | @property 25 | def schedule(self): 26 | return self.__schedule 27 | 28 | @schedule.setter 29 | def schedule(self, value): 30 | self.__schedule = int(value) if value is not None else None 31 | 32 | @validate_args( 33 | value=[of_type(*six.integer_types)], 34 | ) 35 | def set_schedule(self, value): 36 | self.schedule = value 37 | return self 38 | 39 | def __init__( 40 | self, 41 | reason=None, 42 | schedule=None, 43 | ): 44 | super(HangupElement, self).__init__() 45 | 46 | self.reason = reason 47 | self.schedule = schedule 48 | 49 | def to_dict(self): 50 | d = { 51 | 'reason': self.reason, 52 | 'schedule': self.schedule, 53 | } 54 | return { 55 | k: six.text_type(map_type(v)) 56 | for k, v in d.items() if v is not None 57 | } 58 | -------------------------------------------------------------------------------- /plivo/xml/messageElement.py: -------------------------------------------------------------------------------- 1 | from plivo.xml import PlivoXMLElement, map_type 2 | from plivo.utils.validators import * 3 | 4 | 5 | class MessageElement(PlivoXMLElement): 6 | _name = 'Message' 7 | _nestable = [] 8 | 9 | @property 10 | def src(self): 11 | return self.__src 12 | 13 | @src.setter 14 | def src(self, value): 15 | self.__src = six.text_type(value) if value is not None else None 16 | 17 | @validate_args( 18 | value=[of_type(six.text_type)], 19 | ) 20 | def set_src(self, value): 21 | self.src = value 22 | return self 23 | 24 | @property 25 | def dst(self): 26 | return self.__dst 27 | 28 | @dst.setter 29 | def dst(self, value): 30 | self.__dst = six.text_type(value) if value is not None else None 31 | 32 | @validate_args( 33 | value=[of_type(six.text_type)], 34 | ) 35 | def set_dst(self, value): 36 | self.dst = value 37 | return self 38 | 39 | @property 40 | def type(self): 41 | return self.__type 42 | 43 | @type.setter 44 | def type(self, value): 45 | self.__type = six.text_type(value) if value is not None else None 46 | 47 | @validate_args( 48 | value=[of_type(six.text_type)], 49 | ) 50 | def set_type(self, value): 51 | self.type = value 52 | return self 53 | 54 | @property 55 | def callback_url(self): 56 | return self.__callback_url 57 | 58 | @callback_url.setter 59 | def callback_url(self, value): 60 | self.__callback_url = six.text_type( 61 | value) if value is not None else None 62 | 63 | @validate_args( 64 | value=[of_type(six.text_type)], 65 | ) 66 | def set_callback_url(self, value): 67 | self.callback_url = value 68 | return self 69 | 70 | @property 71 | def callback_method(self): 72 | return self.__callback_method 73 | 74 | @callback_method.setter 75 | def callback_method(self, value): 76 | self.__callback_method = six.text_type( 77 | value) if value is not None else None 78 | 79 | @validate_args( 80 | value=[of_type(six.text_type)], 81 | ) 82 | def set_callback_method(self, value): 83 | self.callback_method = value 84 | return self 85 | 86 | def __init__( 87 | self, 88 | content, 89 | src=None, 90 | dst=None, 91 | type=None, 92 | callback_url=None, 93 | callback_method=None, 94 | ): 95 | super(MessageElement, self).__init__() 96 | 97 | self.content = content 98 | self.src = src 99 | self.dst = dst 100 | self.type = type 101 | self.callback_url = callback_url 102 | self.callback_method = callback_method 103 | 104 | def to_dict(self): 105 | d = { 106 | 'src': self.src, 107 | 'dst': self.dst, 108 | 'type': self.type, 109 | 'callbackUrl': self.callback_url, 110 | 'callbackMethod': self.callback_method, 111 | } 112 | return { 113 | k: six.text_type(map_type(v)) 114 | for k, v in d.items() if v is not None 115 | } 116 | -------------------------------------------------------------------------------- /plivo/xml/numberElement.py: -------------------------------------------------------------------------------- 1 | from plivo.xml import PlivoXMLElement, map_type 2 | from plivo.utils.validators import * 3 | 4 | 5 | class NumberElement(PlivoXMLElement): 6 | _name = 'Number' 7 | _nestable = [] 8 | 9 | @property 10 | def send_digits(self): 11 | return self.__send_digits 12 | 13 | @send_digits.setter 14 | def send_digits(self, value): 15 | self.__send_digits = six.text_type( 16 | value) if value is not None else None 17 | 18 | @validate_args( 19 | value=[of_type(six.text_type)], 20 | ) 21 | def set_send_digits(self, value): 22 | self.send_digits = value 23 | return self 24 | 25 | @property 26 | def send_on_preanswer(self): 27 | return self.__send_on_preanswer 28 | 29 | @send_on_preanswer.setter 30 | def send_on_preanswer(self, value): 31 | self.__send_on_preanswer = bool(value) if value is not None else None 32 | 33 | @validate_args( 34 | value=[of_type_exact(bool)], 35 | ) 36 | def set_send_on_preanswer(self, value): 37 | self.send_on_preanswer = value 38 | return self 39 | 40 | @property 41 | def send_digits_mode(self): 42 | return self.__send_digits_mode 43 | 44 | @send_digits_mode.setter 45 | def send_digits_mode(self, value): 46 | self.__send_digits_mode = six.text_type(value) if value is not None else None 47 | 48 | @validate_args( 49 | value=[of_type(six.text_type)], 50 | ) 51 | def set_send_digits_mode(self, value): 52 | self.send_digits_mode = value 53 | return self 54 | 55 | def __init__( 56 | self, 57 | content, 58 | send_digits=None, 59 | send_on_preanswer=None, 60 | send_digits_mode=None): 61 | super(NumberElement, self).__init__() 62 | 63 | self.content = str(content) 64 | self.send_digits = send_digits 65 | self.send_on_preanswer = send_on_preanswer 66 | self.send_digits_mode = send_digits_mode 67 | 68 | def to_dict(self): 69 | d = { 70 | 'sendDigits': self.send_digits, 71 | 'sendOnPreanswer': self.send_on_preanswer, 72 | 'sendDigitsMode': self.send_digits_mode, 73 | } 74 | return { 75 | k: six.text_type(map_type(v)) 76 | for k, v in d.items() if v is not None 77 | } 78 | -------------------------------------------------------------------------------- /plivo/xml/phonemeElement.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import * 2 | from plivo.xml import PlivoXMLElement, map_type 3 | 4 | 5 | class PhonemeElement(PlivoXMLElement): 6 | _name = 'phoneme' 7 | _nestable = [] 8 | 9 | @property 10 | def alphabet(self): 11 | return self.__alphabet 12 | 13 | @alphabet.setter 14 | def alphabet(self, value): 15 | self.__alphabet = six.text_type( 16 | value) if value is not None else None 17 | 18 | @validate_args( 19 | value=[of_type(six.text_type)], 20 | ) 21 | def set_alphabet(self, value): 22 | self.alphabet = value 23 | return self 24 | 25 | @property 26 | def ph(self): 27 | return self.__ph 28 | 29 | @ph.setter 30 | def ph(self, value): 31 | self.__ph = six.text_type( 32 | value) if value is not None else None 33 | 34 | @validate_args( 35 | value=[of_type(six.text_type)], 36 | ) 37 | def set_ph(self, value): 38 | self.ph = value 39 | return self 40 | 41 | def __init__( 42 | self, 43 | content=None, 44 | alphabet=None, 45 | ph=None, 46 | ): 47 | 48 | super(PhonemeElement, self).__init__() 49 | self.content = content 50 | self.alphabet = alphabet 51 | self.ph = ph 52 | 53 | def to_dict(self): 54 | d = { 55 | 'alphabet': self.alphabet, 56 | 'ph': self.ph, 57 | } 58 | 59 | return { 60 | k: six.text_type(map_type(v)) 61 | for k, v in d.items() if v is not None 62 | } 63 | -------------------------------------------------------------------------------- /plivo/xml/playElement.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import * 2 | from plivo.xml import PlivoXMLElement, map_type 3 | 4 | 5 | class PlayElement(PlivoXMLElement): 6 | _name = 'Play' 7 | _nestable = [] 8 | 9 | @property 10 | def loop(self): 11 | return self.__loop 12 | 13 | @loop.setter 14 | def loop(self, value): 15 | self.__loop = int(value) if value is not None else None 16 | 17 | @validate_args( 18 | value=[of_type(*six.integer_types)], 19 | ) 20 | def set_loop(self, value): 21 | self.loop = value 22 | return self 23 | 24 | def __init__( 25 | self, 26 | content, 27 | loop=None, 28 | ): 29 | super(PlayElement, self).__init__() 30 | 31 | self.content = content 32 | self.loop = loop 33 | 34 | def to_dict(self): 35 | d = { 36 | 'loop': self.loop, 37 | } 38 | return { 39 | k: six.text_type(map_type(v)) 40 | for k, v in d.items() if v is not None 41 | } 42 | -------------------------------------------------------------------------------- /plivo/xml/redirectElement.py: -------------------------------------------------------------------------------- 1 | import six 2 | 3 | from plivo.utils.validators import * 4 | from plivo.xml import PlivoXMLElement, map_type 5 | 6 | 7 | class RedirectElement(PlivoXMLElement): 8 | _name = 'Redirect' 9 | _nestable = [] 10 | 11 | @property 12 | def method(self): 13 | return self.__method 14 | 15 | @method.setter 16 | def method(self, value): 17 | self.__method = six.text_type(value) if value is not None else None 18 | 19 | @validate_args( 20 | value=[of_type(six.text_type)], 21 | ) 22 | def set_method(self, value): 23 | self.method = value 24 | return self 25 | 26 | def __init__( 27 | self, 28 | content, 29 | method=None, 30 | ): 31 | super(RedirectElement, self).__init__() 32 | 33 | self.content = content 34 | self.method = method 35 | 36 | def to_dict(self): 37 | d = { 38 | 'method': self.method, 39 | } 40 | return { 41 | k: six.text_type(map_type(v)) 42 | for k, v in d.items() if v is not None 43 | } 44 | -------------------------------------------------------------------------------- /plivo/xml/sayAsElement.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import * 2 | from plivo.xml import PlivoXMLElement, map_type 3 | 4 | 5 | class SayAsElement(PlivoXMLElement): 6 | _name = 'say-as' 7 | _nestable = [] 8 | 9 | @property 10 | def interpret_as(self): 11 | return self.__interpret_as 12 | 13 | @interpret_as.setter 14 | def interpret_as(self, value): 15 | self.__interpret_as = six.text_type( 16 | value) if value is not None else None 17 | 18 | @validate_args( 19 | value=[of_type(six.text_type)], 20 | ) 21 | def set_interpret_as(self, value): 22 | self.interpret_as = value 23 | return self 24 | 25 | @property 26 | def format(self): 27 | return self.__format 28 | 29 | @format.setter 30 | def format(self, value): 31 | self.__format = six.text_type( 32 | value) if value is not None else None 33 | 34 | @validate_args( 35 | value=[of_type(six.text_type)], 36 | ) 37 | def set_format(self, value): 38 | self.format = value 39 | return self 40 | 41 | def __init__( 42 | self, 43 | content, 44 | interpret_as=None, 45 | format=None, 46 | ): 47 | 48 | super(SayAsElement, self).__init__() 49 | self.content = content 50 | self.interpret_as = interpret_as 51 | self.format = format 52 | 53 | def to_dict(self): 54 | d = { 55 | 'interpret-as': self.interpret_as, 56 | 'format': self.format, 57 | } 58 | return { 59 | k: six.text_type(map_type(v)) 60 | for k, v in d.items() if v is not None 61 | } 62 | -------------------------------------------------------------------------------- /plivo/xml/subElement.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import * 2 | from plivo.xml import PlivoXMLElement, map_type 3 | 4 | 5 | class SubElement(PlivoXMLElement): 6 | _name = 'sub' 7 | _nestable = [] 8 | 9 | @property 10 | def alias(self): 11 | return self.__alias 12 | 13 | @alias.setter 14 | def alias(self, value): 15 | self.__alias = six.text_type( 16 | value) if value is not None else None 17 | 18 | @validate_args( 19 | value=[of_type(six.text_type)], 20 | ) 21 | def set_alias(self, value): 22 | self.alias = value 23 | return self 24 | 25 | def __init__( 26 | self, 27 | content=None, 28 | alias=None, 29 | ): 30 | super(SubElement, self).__init__() 31 | 32 | self.content = content 33 | self.alias = alias 34 | 35 | def to_dict(self): 36 | d = { 37 | 'alias': self.alias, 38 | } 39 | 40 | return { 41 | k: six.text_type(map_type(v)) 42 | for k, v in d.items() if v is not None 43 | } 44 | -------------------------------------------------------------------------------- /plivo/xml/userElement.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import * 2 | from plivo.xml import PlivoXMLElement, map_type 3 | 4 | 5 | class UserElement(PlivoXMLElement): 6 | _name = 'User' 7 | _nestable = [] 8 | 9 | @property 10 | def send_digits(self): 11 | return self.__send_digits 12 | 13 | @send_digits.setter 14 | def send_digits(self, value): 15 | self.__send_digits = six.text_type( 16 | value) if value is not None else None 17 | 18 | @validate_args( 19 | value=[of_type(six.text_type)], 20 | ) 21 | def set_send_digits(self, value): 22 | self.send_digits = value 23 | return self 24 | 25 | @property 26 | def send_on_preanswer(self): 27 | return self.__send_on_preanswer 28 | 29 | @send_on_preanswer.setter 30 | def send_on_preanswer(self, value): 31 | self.__send_on_preanswer = bool(value) if value is not None else None 32 | 33 | @validate_args( 34 | value=[of_type_exact(bool)], 35 | ) 36 | def set_send_on_preanswer(self, value): 37 | self.send_on_preanswer = value 38 | return self 39 | 40 | @property 41 | def sip_headers(self): 42 | return self.__sip_headers 43 | 44 | @sip_headers.setter 45 | def sip_headers(self, value): 46 | self.__sip_headers = six.text_type( 47 | value) if value is not None else None 48 | 49 | @validate_args( 50 | value=[of_type(six.text_type)], 51 | ) 52 | def set_sip_headers(self, value): 53 | self.sip_headers = value 54 | return self 55 | 56 | def __init__( 57 | self, 58 | content, 59 | send_digits=None, 60 | send_on_preanswer=None, 61 | sip_headers=None, 62 | ): 63 | super(UserElement, self).__init__() 64 | 65 | self.content = content 66 | self.send_digits = send_digits 67 | self.send_on_preanswer = send_on_preanswer 68 | self.sip_headers = sip_headers 69 | 70 | def to_dict(self): 71 | d = { 72 | 'sendDigits': self.send_digits, 73 | 'sendOnPreanswer': self.send_on_preanswer, 74 | 'sipHeaders': self.sip_headers, 75 | } 76 | return { 77 | k: six.text_type(map_type(v)) 78 | for k, v in d.items() if v is not None 79 | } 80 | -------------------------------------------------------------------------------- /plivo/xml/waitElement.py: -------------------------------------------------------------------------------- 1 | from plivo.utils.validators import * 2 | from plivo.xml import PlivoXMLElement, map_type 3 | 4 | 5 | class WaitElement(PlivoXMLElement): 6 | _name = 'Wait' 7 | _nestable = [] 8 | 9 | @property 10 | def length(self): 11 | return self.__length 12 | 13 | @length.setter 14 | def length(self, value): 15 | self.__length = int(value) if value is not None else None 16 | 17 | @validate_args( 18 | value=[of_type(*six.integer_types)], 19 | ) 20 | def set_length(self, value): 21 | self.length = value 22 | return self 23 | 24 | @property 25 | def silence(self): 26 | return self.__silence 27 | 28 | @silence.setter 29 | def silence(self, value): 30 | self.__silence = bool(value) if value is not None else None 31 | 32 | @validate_args( 33 | value=[of_type_exact(bool)], 34 | ) 35 | def set_silence(self, value): 36 | self.silence = value 37 | return self 38 | 39 | @property 40 | def min_silence(self): 41 | return self.__min_silence 42 | 43 | @min_silence.setter 44 | def min_silence(self, value): 45 | self.__min_silence = int(value) if value is not None else None 46 | 47 | @validate_args( 48 | value=[of_type(*six.integer_types)], 49 | ) 50 | def set_min_silence(self, value): 51 | self.min_silence = value 52 | return self 53 | 54 | @property 55 | def beep(self): 56 | return self.__beep 57 | 58 | @beep.setter 59 | def beep(self, value): 60 | self.__beep = bool(value) if value is not None else None 61 | 62 | @validate_args( 63 | value=[of_type_exact(bool)], 64 | ) 65 | def set_beep(self, value): 66 | self.beep = value 67 | return self 68 | 69 | def __init__( 70 | self, 71 | length=None, 72 | silence=None, 73 | min_silence=None, 74 | beep=None, 75 | ): 76 | super(WaitElement, self).__init__() 77 | 78 | self.length = length 79 | self.silence = silence 80 | self.min_silence = min_silence 81 | self.beep = beep 82 | 83 | def to_dict(self): 84 | d = { 85 | 'length': self.length, 86 | 'silence': self.silence, 87 | 'minSilence': self.min_silence, 88 | 'beep': self.beep, 89 | } 90 | return { 91 | k: six.text_type(map_type(v)) 92 | for k, v in d.items() if v is not None 93 | } 94 | -------------------------------------------------------------------------------- /plivo/xml/xmlUtils.py: -------------------------------------------------------------------------------- 1 | import six 2 | 3 | 4 | def map_type(val): 5 | if isinstance(val, bool): 6 | return six.text_type(val).lower() 7 | return six.text_type(val) 8 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | decorator==5.1.1 2 | lxml==4.2.3 3 | requests==2.20.0 4 | six==1.10.0 5 | PyJWT==2.1.0 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | 4 | [metadata] 5 | description-file = README.md 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | long_description = '''\ 4 | The Plivo Python SDK makes it simpler to integrate communications into your 5 | Python applications using the Plivo REST API. Using the SDK, you will be able 6 | to make voice calls, send SMS and generate Plivo XML to control your call flows. 7 | 8 | See https://github.com/plivo/plivo-python for more information. 9 | ''' 10 | 11 | setup( 12 | name='plivo', 13 | version='4.59.1', 14 | description='A Python SDK to make voice calls & send SMS using Plivo and to generate Plivo XML', 15 | long_description=long_description, 16 | url='https://github.com/plivo/plivo-python', 17 | author='The Plivo SDKs Team', 18 | author_email='sdks@plivo.com', 19 | license='MIT', 20 | classifiers=[ 21 | 'Development Status :: 5 - Production/Stable', 22 | 'Intended Audience :: Developers', 23 | 'Intended Audience :: Telecommunications Industry', 24 | 'License :: OSI Approved :: MIT License', 25 | 'Operating System :: OS Independent', 26 | 'Programming Language :: Python', 27 | 'Programming Language :: Python :: 2', 28 | 'Programming Language :: Python :: 2.7', 29 | 'Programming Language :: Python :: 3', 30 | 'Programming Language :: Python :: 3.4', 31 | 'Programming Language :: Python :: 3.5', 32 | 'Programming Language :: Python :: 3.6', 33 | 'Programming Language :: Python :: 3.7', 34 | 'Programming Language :: Python :: 3.8', 35 | 'Programming Language :: Python :: 3.9', 36 | 'Programming Language :: Python :: 3.11', 37 | 'Programming Language :: Python :: 3.13', 38 | 'Topic :: Software Development :: Libraries :: Python Modules', 39 | 'Topic :: Communications :: Telephony', 40 | ], 41 | install_requires=[ 42 | 'requests >= 2, < 3', 43 | 'six >= 1, < 2', 44 | 'decorator >= 5', 45 | 'lxml >= 3', 46 | 'PyJWT' 47 | ], 48 | keywords=['plivo', 'plivo xml', 'voice calls', 'sms'], 49 | include_package_data=True, 50 | packages=find_packages(exclude=['tests', 'tests.*']), ) 51 | -------------------------------------------------------------------------------- /setup_sdk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | testDir="python-sdk-test" 5 | GREEN="\033[0;32m" 6 | NC="\033[0m" 7 | 8 | if [ ! $PLIVO_API_PROD_HOST ] || [ ! $PLIVO_API_DEV_HOST ] || [ ! $PLIVO_AUTH_ID ] || [ ! $PLIVO_AUTH_TOKEN ]; then 9 | echo "Environment variables not properly set! Please refer to Local Development section in README!" 10 | exit 126 11 | fi 12 | 13 | cd /usr/src/app 14 | 15 | echo "Setting plivo-api endpoint to dev..." 16 | find /usr/src/app/plivo/rest/ -type f -exec sed -i "s/$PLIVO_API_PROD_HOST/$PLIVO_API_DEV_HOST/g" {} \; 17 | find /usr/src/app/tests/ -type f -exec sed -i "s/$PLIVO_API_PROD_HOST/$PLIVO_API_DEV_HOST/g" {} \; 18 | 19 | if [ ! -d $testDir ]; then 20 | echo "Creating test dir..." 21 | mkdir -p $testDir 22 | fi 23 | 24 | if [ ! -f $testDir/test.py ]; then 25 | echo "Creating test file..." 26 | cd $testDir 27 | echo 'import sys' > test.py 28 | echo 'sys.path.append("../")' >> test.py 29 | echo 'import plivo' >> test.py 30 | cd - 31 | fi 32 | 33 | echo "Installing dependencies for testing..." 34 | pip install -r requirements.txt 35 | pip install tox coverage # For unit tests 36 | /bin/bash package.sh 37 | 38 | echo -e "\n\nSDK setup complete! You can test changes either on host or inside the docker container:" 39 | echo -e "\ta. To test your changes ON HOST:" 40 | echo -e "\t\t1. Add your test code in /$testDir/test.py" 41 | echo -e "\t\t2. Run your test file using: $GREEN make run CONTAINER=$HOSTNAME$NC" 42 | echo -e "\t\t3. Run unit tests using: $GREEN make test CONTAINER=$HOSTNAME$NC" 43 | echo 44 | echo -e "\tb. To test your changes INSIDE CONTAINER:" 45 | echo -e "\t\t1. Run a terminal in the container using: $GREEN docker exec -it $HOSTNAME /bin/bash$NC" 46 | echo -e "\t\t2. Add your test code in /usr/src/app/$testDir/test.py" 47 | echo -e "\t\t3. Run your test file using: $GREEN make run$NC" 48 | echo -e "\t\t4. Run unit tests using: $GREEN make test$NC" 49 | 50 | # To keep the container running post setup 51 | /bin/bash -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .base import * 3 | from .resources import * 4 | from .decorators import * 5 | from .xml import * -------------------------------------------------------------------------------- /tests/decorators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import functools 3 | import io 4 | import json 5 | import os 6 | 7 | 8 | def with_response(status_code, method_name=None): 9 | def wrapper(func): 10 | @functools.wraps(func) 11 | def decorator(self, *args, **kwargs): 12 | name = method_name or func.__name__.replace('test_', '') 13 | name = self.__class__.__name__.replace('Test', '') + name.replace( 14 | '_', ' ').title().replace(' ', '') 15 | name = name[0].lower() + name[1:] + 'Response' 16 | 17 | path = os.path.abspath( 18 | os.path.join( 19 | os.path.dirname(__file__), 'resources', 'fixtures', 20 | name + '.json')) 21 | 22 | try: 23 | with io.open(path, encoding='utf-8') as f: 24 | self.expected_response = json.load(f) 25 | self.client.set_expected_response( 26 | status_code=status_code, 27 | data_to_return=self.expected_response) 28 | except IOError: 29 | if 'delete' in func.__name__: 30 | self.client.set_expected_response( 31 | data_to_return=None, status_code=status_code) 32 | 33 | func(self, *args, **kwargs) 34 | 35 | return decorator 36 | 37 | return wrapper 38 | -------------------------------------------------------------------------------- /tests/resources/__init__.py: -------------------------------------------------------------------------------- 1 | from .test_applications import * 2 | from .test_accounts import * 3 | from .test_calls import * 4 | from .test_client import * 5 | from .test_conferences import * 6 | from .test_endpoints import * 7 | from .test_messages import * 8 | from .test_powerpacks import * 9 | from .test_medias import * 10 | from .test_multipartycalls import * 11 | from .test_numbers import * 12 | from .test_pricings import * 13 | from .test_recordings import * 14 | from .test_signature import * 15 | from .test_subaccounts import * 16 | from .test_brand import * 17 | from .test_campaign import * 18 | from .test_verify import * -------------------------------------------------------------------------------- /tests/resources/fixtures/accountGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_type": "standard", 3 | "address": "", 4 | "api_id": "95e1df78-3f08-11e7-8bc8-065f6a74a84a", 5 | "auth_id": "MAXXXXXXXXXXXXXXXXXX", 6 | "auto_recharge": false, 7 | "billing_mode": "prepaid", 8 | "cash_credits": "4.88860", 9 | "city": "", 10 | "name": "Sherlock Holmes", 11 | "resource_uri": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/", 12 | "state": "", 13 | "timezone": "Asia/Kolkata" 14 | } 15 | -------------------------------------------------------------------------------- /tests/resources/fixtures/accountUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "964edb6e-3f08-11e7-920b-0600a1193e9b", 3 | "message": "changed" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/addressCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "6b2784b3-b537-42be-89d1-f652783ed642", 3 | "message": "Your request has been accepted." 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/addressGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "account": "MAXXXXXXXXXXXXXXXXXX", 3 | "address_line1": "128", 4 | "address_line2": "RUE DU COMMANDANT GUILBAUD", 5 | "alias": "test_address", 6 | "api_id": "08361d92-0b59-11e8-aa7e-02ad5072be3e", 7 | "city": "PARIS", 8 | "country_iso": "FR", 9 | "document_details": { 10 | "address_line1": "128", 11 | "address_line2": "RUE DU COMMANDANT GUILBAUD", 12 | "city": "PARIS", 13 | "first_name": "Bruce", 14 | "last_name": "Wayne", 15 | "postal_code": "75016", 16 | "region": "PARIS", 17 | "salutation": "Mr" 18 | }, 19 | "first_name": "Bruce", 20 | "id": "20220771838737", 21 | "last_name": "Wayne", 22 | "postal_code": "75016", 23 | "region": "PARIS", 24 | "salutation": "Mr", 25 | "subaccount": null, 26 | "url": "https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Verification/Address/20220771838737/", 27 | "validation_status": "accepted", 28 | "verification_status": "pending" 29 | } 30 | -------------------------------------------------------------------------------- /tests/resources/fixtures/addressListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "af8b199a-0b58-11e8-b939-06755d68f0ca", 3 | "meta": { 4 | "limit": 1, 5 | "next": "https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Verification/Address/?limit=1&offset=1", 6 | "offset": 0, 7 | "previous": null, 8 | "total_count": 22 9 | }, 10 | "objects": [ 11 | { 12 | "account": "MAXXXXXXXXXXXXXXXXXX", 13 | "address_line1": "128", 14 | "address_line2": "RUE DU COMMANDANT GUILBAUD", 15 | "alias": "test_address", 16 | "city": "PARIS", 17 | "country_iso": "FR", 18 | "document_details": { 19 | "address_line1": "128", 20 | "address_line2": "RUE DU COMMANDANT GUILBAUD", 21 | "city": "PARIS", 22 | "first_name": "Bruce", 23 | "last_name": "Wayne", 24 | "postal_code": "75016", 25 | "region": "PARIS", 26 | "salutation": "Mr" 27 | }, 28 | "first_name": "Bruce", 29 | "id": "20220771838737", 30 | "last_name": "Wayne", 31 | "postal_code": "75016", 32 | "region": "PARIS", 33 | "salutation": "Mr", 34 | "subaccount": null, 35 | "url": "https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Verification/Address/20220771838737/", 36 | "validation_status": "accepted", 37 | "verification_status": "pending" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /tests/resources/fixtures/addressUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "6b2784b3-b537-42be-89d1-f652783ed642", 3 | "message": "Your request has been accepted.", 4 | "status": "success" 5 | } 6 | -------------------------------------------------------------------------------- /tests/resources/fixtures/applicationCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "99f9d6f6-3f08-11e7-9fd1-06660ad2b8e6", 3 | "app_id": "20468599130939380", 4 | "message": "created" 5 | } 6 | -------------------------------------------------------------------------------- /tests/resources/fixtures/applicationGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "answer_method": "POST", 3 | "answer_url": "http://www.google.com/", 4 | "api_id": "9ae358d0-3f08-11e7-b6f4-061564b78b75", 5 | "app_id": "20468599130939380", 6 | "app_name": "0.8522478089992022", 7 | "default_app": false, 8 | "default_endpoint_app": false, 9 | "enabled": true, 10 | "fallback_answer_url": "", 11 | "fallback_method": "POST", 12 | "hangup_method": "POST", 13 | "hangup_url": "http://www.google.com/", 14 | "message_method": "POST", 15 | "message_url": "", 16 | "public_uri": false, 17 | "resource_uri": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/Application/20468599130939380/", 18 | "sip_uri": "sip:20468599130939380@app.plivo.com", 19 | "sub_account": null 20 | } 21 | -------------------------------------------------------------------------------- /tests/resources/fixtures/applicationModifyResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "9b43ea74-3f08-11e7-8bc8-065f6a74a84a", 3 | "message": "changed" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/brandCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "7772d11a-0725-11ed-82e9-0242ac110004", 3 | "brand_id": "B43KNAS", 4 | "message": "Request to create brand was received and is being processed." 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/brandDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "Brand Deactivated", 3 | "brand_id": "BRPXS6E" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/brandGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "e1474046-0723-11ed-ab02-0242ac110004", 3 | "brand": { 4 | "address": { 5 | "city": "New York", 6 | "country": "IN", 7 | "postal_code": "10001", 8 | "state": "NY", 9 | "street": "123" 10 | }, 11 | "authorized_contact": { 12 | "email": "test@plivo.com", 13 | "first_name": "John", 14 | "last_name": "Doe", 15 | "phone": "919074079045", 16 | "seniority": "admin", 17 | "title": "Doe" 18 | }, 19 | "brand_id": "BOZ5T2X", 20 | "brand_type": "STARTER", 21 | "ein_issuing_country": "IN", 22 | "entity_type": "INDIVIDUAL", 23 | "profile_uuid": "6e9a16c7-27ae-44b4-8b1f-e6b9b007426b", 24 | "registration_status": "COMPLETED", 25 | "vertical": "ENERGY" 26 | } 27 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/brandGetUsecasesResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "use_cases": [ 3 | { 4 | "name": "Account Notification", 5 | "code": "ACCOUNT_NOTIFICATION", 6 | "details": "Notification sent to account holders about changes in accounts" 7 | }, 8 | { 9 | "name": "Customer Care", 10 | "code": "CUSTOMER_CARE", 11 | "details": "Customer care interactions by the support and other customer-facing teams" 12 | }, 13 | { 14 | "name": "Delivery Notification", 15 | "code": "DELIVERY_NOTIFICATION", 16 | "details": "Updates about the delivery of products and services" 17 | }, 18 | { 19 | "name": "Fraud Alert", 20 | "code": "FRAUD_ALERT", 21 | "details": "Notifications of suspicious behavior identified the business" 22 | }, 23 | { 24 | "name": "Higher Education", 25 | "code": "HIGHER_EDUCATION", 26 | "details": "Messages sent by colleges, universities, and other educational institutions" 27 | }, 28 | { 29 | "name": "Low Volume", 30 | "code": "LOW_VOLUME", 31 | "details": "A combination of two to five standard usage cases - for low throughput requirements" 32 | }, 33 | { 34 | "name": "Marketing", 35 | "code": "MARKETING", 36 | "details": "Communications related to time-bound events and sales" 37 | }, 38 | { 39 | "name": "Mixed", 40 | "code": "MIXED", 41 | "details": "A combination of two to five standard usage cases" 42 | }, 43 | { 44 | "name": "Polling Voting", 45 | "code": "POLLING_VOTING", 46 | "details": "Surveys, polling, and voting campaigns used for non-political purposes" 47 | }, 48 | { 49 | "name": "Public Service Announcement", 50 | "code": "PUBLIC_SERVICE_ANNOUNCEMENT", 51 | "details": "Messages aimed at creating awareness about important topics" 52 | }, 53 | { 54 | "name": "Security Alert", 55 | "code": "SECURITY_ALERT", 56 | "details": "Notifications that alert users about a potential breach of systems" 57 | } 58 | ], 59 | "brand_id": "BRPXS6E", 60 | "api_id": "e1474046-0723-11ed-ab02-0242ac110004" 61 | } 62 | -------------------------------------------------------------------------------- /tests/resources/fixtures/callCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "call fired", 3 | "request_uuid": "9834029e-58b6-11e1-b8b7-a5bd0e4e126f", 4 | "api_id": "97ceeb52-58b6-11e1-86da-77300b68f8bb" 5 | } 6 | -------------------------------------------------------------------------------- /tests/resources/fixtures/callGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "answer_time": null, 3 | "api_id": "95727cf4-3eeb-11e7-8edf-02ed609bd62b", 4 | "bill_duration": 0, 5 | "billed_duration": 0, 6 | "call_direction": "outbound", 7 | "call_duration": 0, 8 | "call_uuid": "4d04c52e-cea3-4458-bbdb-0bfc314ee7cd", 9 | "end_time": "2017-05-22 17:49:40+05:30", 10 | "from_number": "+919879879876", 11 | "initiation_time": "2017-05-22 17:49:30+05:30", 12 | "parent_call_uuid": null, 13 | "resource_uri": "/v1/Account/MAJHUDTEYWLSIUYTDBCZ/Call/4d04c52e-cea3-4458-bbdb-0bfc314ee7cd/", 14 | "to_number": "919999999999", 15 | "total_amount": "0.00000", 16 | "total_rate": "0.03570" 17 | } 18 | -------------------------------------------------------------------------------- /tests/resources/fixtures/callListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "8299d094-dc72-11e5-b56c-22000ae90795", 3 | "meta": { 4 | "limit": 20, 5 | "next": null, 6 | "offset": 0, 7 | "previous": null, 8 | "total_count": 4 9 | }, 10 | "objects": [{ 11 | "answer_time": "2015-07-26 15:45:02+05:30", 12 | "api_id": "06ae0f8f-dc72-11e5-b56c-22000ae90795", 13 | "bill_duration": 924, 14 | "billed_duration": 960, 15 | "call_direction": "outbound", 16 | "call_duration": 924, 17 | "call_uuid": "eba53b9e-8fbd-45c1-9444-696d2172fbc8", 18 | "end_time": "2015-07-26 15:45:14+05:30", 19 | "from_number": "+14158572518", 20 | "initiation_time": "2015-07-26 15:44:49+05:30", 21 | "parent_call_uuid": null, 22 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Call/eba53b9e-8fbd-45c1-9444-696d2172fbc8/", 23 | "to_number": "14153268174", 24 | "total_amount": "0.13600", 25 | "total_rate": "0.00850" 26 | }, 27 | { 28 | "answer_time": "2015-07-26 16:45:02+05:30", 29 | "api_id": "06ae0f8f-dc72-11e5-b56c-22000ae90795", 30 | "bill_duration": 924, 31 | "billed_duration": 960, 32 | "call_direction": "outbound", 33 | "call_duration": 924, 34 | "call_uuid": "eba53b9e-8fbd-45c1-9444-696d2172fbc8", 35 | "end_time": "2015-07-26 16:45:14+05:30", 36 | "from_number": "+14158572518", 37 | "initiation_time": "2015-07-26 16:44:49+05:30", 38 | "parent_call_uuid": null, 39 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Call/eba53b9e-8fbd-45c1-9444-696d2172fbc8/", 40 | "to_number": "14153268174", 41 | "total_amount": "0.13600", 42 | "total_rate": "0.00850" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/callUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "call transfered", 3 | "api_id": "95727cf4-3eeb-11e7-86da-adf28403fe48" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/campaignCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "c520bd82-0725-11ed-82e9-0242ac110004", 3 | "campaign_id": "CAOHTU5", 4 | "message": "Request to create campaign was received and is being processed." 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/campaignDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "Campaign Deactivated", 3 | "campaign_id": "CUU5RCB" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/campaignGetNumberResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "ca143ac8-072d-11ed-ab02-0242ac110004", 3 | "error": "The number xxxxxx is not linked to this campaign" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/campaignGetNumbersResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "e50136b2-b168-11ec-80b6-0242ac110003", 3 | "campaign_alias": "marketing messages", 4 | "campaign_id": "CRIGC80", 5 | "phone_numbers": [ 6 | { 7 | "number": "14845007032", 8 | "status": "PROCESSING" 9 | }, 10 | { 11 | "number": "14844963797", 12 | "status": "COMPLETED" 13 | } 14 | ], 15 | "usecase": "MIXED" 16 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/campaignGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "f687a17e-0725-11ed-ab02-0242ac110004", 3 | "campaign": { 4 | "brand_id": "BOZ5T2X", 5 | "campaign_id": "CY5NVUA", 6 | "mno_metadata": { 7 | "AT&T": { 8 | "tpm": 15 9 | }, 10 | "T-Mobile": { 11 | "brand_tier": "SOLE_PROPRIETOR" 12 | }, 13 | "US Cellular": { 14 | "tpm": 15 15 | }, 16 | "Verizon Wireless": { 17 | "tpm": 15 18 | } 19 | }, 20 | "registration_status": "ACTIVE", 21 | "reseller_id": "", 22 | "sub_usecase": "2FA,MARKETING", 23 | "usecase": "STARTER", 24 | "campaign_source": "plivo" 25 | } 26 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/campaignImportResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "2c6c5e16-090a-11ed-bb48-0242ac110004", 3 | "campaign_id": "CNTQ0OD", 4 | "message": "Request to import campaign was received and is being processed." 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/campaignNumberLinkResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "5cd66c88-ba16-11ec-8e19-0242ac110003", 3 | "error": "The number xxxx is already requested for linking" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/campaignNumberUnlinkResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "efd83afc-072d-11ed-ab02-0242ac110004", 3 | "error": "The number xxxxx is not a valid number" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/campaignUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "campaign": { 3 | "campaign_id": "CXNSG9W", 4 | "registration_status": "FAILED", 5 | "reseller_id": "", 6 | "brand_id": "BS2TTMI", 7 | "usecase": "MARKETING", 8 | "mno_metadata": { 9 | "AT&T": { 10 | "tpm": 4500 11 | }, 12 | "T-Mobile": { 13 | "brand_tier": "TOP" 14 | }, 15 | "US Cellular": { 16 | "tpm": 4500 17 | }, 18 | "Verizon Wireless": { 19 | "tpm": 4500 20 | } 21 | }, 22 | "sample1": "updated sample1 asdsdasdfasdsdasdf 2", 23 | "sample2": "sample message sdasdasdasdsdasdasda 2", 24 | "description": "campaign descriptioncampaign descriptioncampaign descriptioncampaign descriptioncampaign descriptioncampaign descriptioncampaign descriptioncampaign descriptioncampaign description", 25 | "campaign_attributes": { 26 | "embedded_link": false, 27 | "embedded_phone": false, 28 | "age_gated": false, 29 | "direct_lending": false, 30 | "subscriber_optin": false, 31 | "subscriber_optout": false, 32 | "subscriber_help": false, 33 | "affiliate_marketing": false 34 | }, 35 | "message_flow": "message flowmessage flowmessage flowmessage flowmessage flowmessage flowmessage flowmessage flowmessage flow", 36 | "help_message": "help message 2help message 2help message 2help message 2help message 2help message 2help message 2help message 2", 37 | "help_keywords": "HELP UPDATE" 38 | } 39 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceDeleteAllResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "all conferences hung up", 3 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "conference hung up", 3 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "conference_name": "My Conf Room", 3 | "conference_run_time": "590", 4 | "conference_member_count": "1", 5 | "members": [{ 6 | "muted": false, 7 | "member_id": "17", 8 | "deaf": false, 9 | "from": "1456789903", 10 | "to": "1677889900", 11 | "caller_name": "John", 12 | "direction": "inbound", 13 | "call_uuid": "acfbf0b5-12e0-4d74-85f7-fce15f8f07ec", 14 | "join_time": "590" 15 | }], 16 | "api_id": "816e903e-58c4-11e1-86da-adf28403fe48" 17 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48", 3 | "conferences": [ 4 | "My Conf Room" 5 | ] 6 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceMemberDeafCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "deaf", 3 | "member_id": "10", 4 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceMemberDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "hangup", 3 | "member_id": "10", 4 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceMemberKickCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "kicked", 3 | "member_id": "10", 4 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48" 5 | } 6 | -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceMemberMuteCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "muted", 3 | "member_id": "10", 4 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48" 5 | } 6 | -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceMemberPlayCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "play queued into conference", 3 | "api_id": "4e44bd4e-f830-11e6-b886-067c5485c240", 4 | "member_id": "[u'160005', u'160004', u'160003', u'160002']" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceMemberPlayDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "playing in conference stopped", 3 | "member_id": "10", 4 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48" 5 | } 6 | -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceMemberSpeakCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "speak queued into conference", 3 | "api_id": "8dd6820e-fe83-11e6-b6f4-061564b78b75", 4 | "member_id": "[u'all']" 5 | } 6 | -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceMemberSpeakDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "speak stopped", 3 | "member_id": "10", 4 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48" 5 | } 6 | -------------------------------------------------------------------------------- /tests/resources/fixtures/conferenceRecordCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "2867b6e2-58c3-11e1-86da-adf28403fe48", 3 | "message": "conference recording started", 4 | "recording_id": "93bc7c6a-3b2b-11e3", 5 | "url": "http://s3.amazonaws.com/recordings_2013/93bc7c6a-3b2b-11e3.mp3" 6 | } 7 | -------------------------------------------------------------------------------- /tests/resources/fixtures/endpointCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "username": "zumba131031145958", 3 | "alias": "zumba", 4 | "message": "created", 5 | "endpoint_id": "37371860103666", 6 | "api_id": "1c13de4c-423d-11e3-9899-22000abfa5d5" 7 | } 8 | -------------------------------------------------------------------------------- /tests/resources/fixtures/endpointGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "alias": "callme", 3 | "application": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Application/33406267401237901/", 4 | "endpoint_id": "32866729519064", 5 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Endpoint/32866729519064/", 6 | "sip_contact": "sip:callme140703093224@122.172.71.207:57563;ob", 7 | "sip_expires": "2014-07-21 19:26:08", 8 | "sip_registered": "true", 9 | "sip_uri": "sip:callme140703093944@phone.plivo.com", 10 | "sip_user_agent": "Telephone 1.1.4", 11 | "sub_account": null, 12 | "username": "callme140703093944" 13 | } 14 | -------------------------------------------------------------------------------- /tests/resources/fixtures/endpointListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "30a0c8c2-110c-11e4-bd8a-12313f016a39", 3 | "meta": { 4 | "limit": 20, 5 | "next": null, 6 | "offset": 0, 7 | "previous": null, 8 | "total_count": 2 9 | }, 10 | "objects": [ 11 | { 12 | "alias": "callme", 13 | "application": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Application/33406267401237901/", 14 | "endpoint_id": "32866729519064", 15 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Endpoint/32866729519064/", 16 | "sip_contact": "sip:callme140703093224@122.172.71.207:57563;ob", 17 | "sip_expires": "2014-07-21 19:26:08", 18 | "sip_registered": "true", 19 | "sip_uri": "sip:callme140703093944@phone.plivo.com", 20 | "sip_user_agent": "Telephone 1.1.4", 21 | "sub_account": null, 22 | "username": "callme140703093944" 23 | }, 24 | { 25 | "alias": "polycom", 26 | "application": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Application/37961981447734951/", 27 | "endpoint_id": "17551316589618", 28 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Endpoint/17551316589618/", 29 | "sip_registered": "false", 30 | "sip_uri": "sip:polycom140506175228@phone.plivo.com", 31 | "sub_account": null, 32 | "username": "polycom140506175448" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /tests/resources/fixtures/endpointUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "changed", 3 | "api_id": "d8f9ea6c-58cc-11e1-86da-adf28403fe48" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/identityCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "a9318c3e-3fad-47d3-b78a-4d38d2288ff5", 3 | "message": "Your request has been accepted." 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/identityGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "account": "MAXXXXXXXXXXXXXXXXXX", 3 | "alias": null, 4 | "api_id": "6e951a10-0b5a-11e8-a416-06f8d2f9b24c", 5 | "country_iso": "FR", 6 | "document_details": { 7 | "first_name": "Bruce", 8 | "id_number": "33522", 9 | "id_type": "passport", 10 | "last_name": "Wayne", 11 | "nationality": "FR", 12 | "salutation": "Mr" 13 | }, 14 | "first_name": "Bruce", 15 | "id": "24856289978366", 16 | "id_number": "33522", 17 | "id_type": "passport", 18 | "last_name": "Wayne", 19 | "nationality": "FR", 20 | "salutation": "Mr", 21 | "subaccount": null, 22 | "url": "https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Verification/Identity/24856289978366/", 23 | "validation_status": "pending", 24 | "verification_status": "pending" 25 | } 26 | -------------------------------------------------------------------------------- /tests/resources/fixtures/identityListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "3c294e0c-0b5a-11e8-9ad8-064c8d7b2d26", 3 | "meta": { 4 | "limit": 1, 5 | "next": "https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Verification/Identity/?limit=1&offset=1", 6 | "offset": 0, 7 | "previous": null, 8 | "total_count": 6 9 | }, 10 | "objects": [ 11 | { 12 | "account": "MAXXXXXXXXXXXXXXXXXX", 13 | "alias": null, 14 | "country_iso": "FR", 15 | "document_details": { 16 | "first_name": "Bruce", 17 | "id_number": "33522", 18 | "id_type": "passport", 19 | "last_name": "Wayne", 20 | "nationality": "FR", 21 | "salutation": "Mr" 22 | }, 23 | "first_name": "Bruce", 24 | "id": "24856289978366", 25 | "id_number": "33522", 26 | "id_type": "passport", 27 | "last_name": "Wayne", 28 | "nationality": "FR", 29 | "salutation": "Mr", 30 | "subaccount": null, 31 | "url": "https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Verification/Identity/24856289978366/", 32 | "validation_status": "pending", 33 | "verification_status": "pending" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tests/resources/fixtures/identityUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "3a869c12-f65a-42e9-a455-9919a42dbd89", 3 | "message": "Your request has been accepted.", 4 | "status": "success" 5 | } 6 | -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallDtmfCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "digits sent", 3 | "api_id": "07abfd94-58c0-11e1-86da-adf28403fe48" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "18f3bbb0-3ef1-11e7-a68e-02d2f90c026e", 3 | "call_status": "in-progress", 4 | "call_uuid": "d0a87a1a-b0e9-4ab2-ac07-c22ee87cd04a", 5 | "caller_name": "", 6 | "direction": "outbound", 7 | "from": "+918687888990", 8 | "request_uuid": "d0a87a1a-b0e9-4ab2-ac07-c22ee87cd04a", 9 | "session_start": "2017-05-22 13:17:49.050872", 10 | "to": "919798990001" 11 | } 12 | -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallListGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "b251cbd6-3ef0-11e7-a51d-0245fa790d9e", 3 | "calls": [ 4 | "5bd78c3c-5020-4a32-a495-3af5a765ded5" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallPlayCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "play started", 3 | "api_id": "69487be8-3ef4-11e7-a51d-0245fa790d9e" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallRecordCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "http://s3.amazonaws.com/recordings_2013/48dfaf60-3b2a-11e3.mp3", 3 | "message": "call recording started", 4 | "recording_id": "48dfaf60-3b2a-11e3", 5 | "api_id": "c7b69074-58be-11e1-86da-adf28403fe48" 6 | } 7 | -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallSpeakCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "speak started", 3 | "api_id": "4d14465e-3ef5-11e7-a68e-02d2f90c026e" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallSpeakDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "speak stopped", 3 | "api_id": "908173d0-3ef5-11e7-a68e-02d2f90c026e" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallStreamCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "ff09383e-246f-11ed-a1fe-0242ac110004", 3 | "message": "audio streaming started", 4 | "stream_id": "30852c23-ee79-4eb4-b7b9-cd360545965b" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallStreamDeleteAllResponse.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tests/resources/fixtures/liveCallStreamGetAllResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "8d1d27f0-24f8-11ed-8311-0242ac11000b", 3 | "meta": { 4 | "count": 1, 5 | "limit": 20, 6 | "next": null, 7 | "offset": 0, 8 | "previous": null 9 | }, 10 | "objects": [ 11 | { 12 | "call_uuid": "4f045f7a-b04a-4364-8523-74e6072f4f72", 13 | "end_time": null, 14 | "service_url": "ws://3ee3-106-51-87-58.ngrok.io", 15 | "start_time": null, 16 | "status": "initiated", 17 | "status_callback_url": "", 18 | "stream_id": "c436abf8-e8a1-4d96-9aa8-b9143f7f3517" 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/lookupGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "a35a2e7a-fd27-42e6-910c-33382f43240e", 3 | "phone_number": "+14154305555", 4 | "country": { 5 | "name": "United States", 6 | "iso2": "US", 7 | "iso3": "USA" 8 | }, 9 | "format": { 10 | "e164": "+14154305555", 11 | "national": "(415) 430-5555", 12 | "international": "+1 415-430-5555", 13 | "rfc3966": "tel:+1-415-430-5555" 14 | }, 15 | "carrier": { 16 | "mobile_country_code": "310", 17 | "mobile_network_code": "150", 18 | "name": "Cingular Wireless", 19 | "type": "mobile", 20 | "ported": "yes" 21 | }, 22 | "resource_uri": "/v1/Number/+14154305555?type=carrier" 23 | } 24 | -------------------------------------------------------------------------------- /tests/resources/fixtures/maskingSessionCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "7fef62d3-a451-40ef-95e7-b4ad9969aa1e", 3 | "session_uuid": "bc0a20bb-9f09-4116-b3f8-709def3a89df", 4 | "virtual_number": "+916361728680", 5 | "message": "Session created", 6 | "session": { 7 | "first_party": "917708772011", 8 | "second_party": "919976106830", 9 | "virtual_number": "916361728680", 10 | "status": "active", 11 | "initiate_call_to_first_party": false, 12 | "session_uuid": "bc0a20bb-9f09-4116-b3f8-709def3a89df", 13 | "callback_url": "https://s3.amazonaws.com/static.plivo.com/callback.xml", 14 | "callback_method": "GET", 15 | "created_time": "2023-07-12 15:52:14.772699 +0000 UTC", 16 | "modified_time": "2023-07-12 15:52:14.772699 +0000 UTC", 17 | "expiry_time": "2023-07-12 17:32:14.772699 +0000 UTC", 18 | "duration": 6000, 19 | "amount": 0, 20 | "call_time_limit": 14400, 21 | "ring_timeout": 120, 22 | "first_party_play_url": "https://s3.amazonaws.com/plivosamplexml/play_url.xml", 23 | "second_party_play_url": "https://s3.amazonaws.com/plivosamplexml/play_url.xml", 24 | "record": false, 25 | "record_file_format": "mp3", 26 | "recording_callback_url": "https://s3.amazonaws.com/static.plivo.com/callback.xml", 27 | "recording_callback_method": "GET", 28 | "interaction": null, 29 | "total_call_amount": 0, 30 | "total_call_count": 0, 31 | "total_call_billed_duration": 0, 32 | "total_session_amount": 0, 33 | "last_interaction_time": "", 34 | "is_pin_authentication_required": true, 35 | "generate_pin": false, 36 | "generate_pin_length": 4, 37 | "first_party_pin": "1234", 38 | "second_party_pin": "1235", 39 | "pin_prompt_play": "https://s3.amazonaws.com/plivosamplexml/pin_prompt_play_url.xml", 40 | "pin_retry": 2, 41 | "pin_retry_wait": 5, 42 | "incorrect_pin_play": "https://s3.amazonaws.com/plivosamplexml/incorrect_play_url.xml", 43 | "unknown_caller_play": "https://s3.amazonaws.com/plivosamplexml/unknown_play_url.xml" 44 | } 45 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/maskingSessionDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "f91f459a-283d-4850-9b33-ad914dcb64c0", 3 | "message": "Session Deleted" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/maskingSessionGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "b503c0b9-a419-406f-8e49-9856844600ab", 3 | "response": { 4 | "first_party": "917708772011", 5 | "second_party": "919976106830", 6 | "virtual_number": "916361728680", 7 | "status": "active", 8 | "initiate_call_to_first_party": false, 9 | "session_uuid": "c2146ba4-798d-49b0-8580-53851a16e055", 10 | "callback_url": "https://s3.amazonaws.com/static.plivo.com/callback.xml", 11 | "callback_method": "GET", 12 | "created_time": "2023-07-05 10:25:40.877364 +0000 UTC", 13 | "modified_time": "2023-07-05 10:25:40.877364 +0000 UTC", 14 | "expiry_time": "2023-07-05 12:05:40.877364 +0000 UTC", 15 | "duration": 6000, 16 | "amount": 0, 17 | "call_time_limit": 14400, 18 | "ring_timeout": 120, 19 | "first_party_play_url": "https://s3.amazonaws.com/plivosamplexml/play_url.xml", 20 | "second_party_play_url": "https://s3.amazonaws.com/plivosamplexml/play_url.xml", 21 | "record": false, 22 | "record_file_format": "mp3", 23 | "recording_callback_url": "https://s3.amazonaws.com/static.plivo.com/callback.xml", 24 | "recording_callback_method": "GET", 25 | "interaction": null, 26 | "total_call_amount": 0, 27 | "total_call_count": 0, 28 | "total_call_billed_duration": 0, 29 | "total_session_amount": 0, 30 | "last_interaction_time": "" 31 | } 32 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/maskingSessionUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "b5506536-83d0-498f-929f-4427cb6ca391", 3 | "message": "Session updated", 4 | "session": { 5 | "first_party": "917708772011", 6 | "second_party": "919976106830", 7 | "virtual_number": "916361728680", 8 | "status": "active", 9 | "initiate_call_to_first_party": false, 10 | "session_uuid": "7b5c5e17-e1e9-4ccd-a480-42f5c97fbe96", 11 | "callback_url": "https://s3.amazonaws.com/static.plivo.com/callback.xml", 12 | "callback_method": "GET", 13 | "created_time": "2023-07-06 10:53:32.814078 +0000 +0000", 14 | "modified_time": "2023-07-06 10:53:45.106122 +0000 UTC", 15 | "expiry_time": "2023-07-06 11:03:45.106117 +0000 UTC", 16 | "duration": 612, 17 | "amount": 0, 18 | "call_time_limit": 14600, 19 | "ring_timeout": 120, 20 | "first_party_play_url": "https://s3.amazonaws.com/plivosamplexml/play_url.xml", 21 | "second_party_play_url": "https://s3.amazonaws.com/plivosamplexml/play_url.xml", 22 | "record": true, 23 | "record_file_format": "mp3", 24 | "recording_callback_url": "https://s3.amazonaws.com/static.plivo.com/callback.xml", 25 | "recording_callback_method": "GET", 26 | "interaction": null, 27 | "total_call_amount": 0, 28 | "total_call_count": 0, 29 | "total_call_billed_duration": 0, 30 | "total_session_amount": 0, 31 | "last_interaction_time": "" 32 | } 33 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/mediaGetMediaResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "292437c0-e5a3-11e9-b521-0242ac110003", 3 | "content_type": "image/gif", 4 | "file_name": "SampleFile.png", 5 | "media_id": "91f8f9ce-e236-474f-a2b8-f75d9c35988a", 6 | "size": 700670, 7 | "upload_time": "2020-02-17T07:53:36.643522Z", 8 | "media_url": "https://media.plivo.com/Account/MAODZKMDFJMJU3MTEYNG/Media/91f8f9ce-e236-474f-a2b8-f75d9c35988a" 9 | } 10 | -------------------------------------------------------------------------------- /tests/resources/fixtures/mediaListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "95e98e4a-e5a4-11e9-b521-0242ac110003", 3 | "meta": { 4 | "limit": 20, 5 | "next": "/v1/Account//Media?offset=20&limit=20", 6 | "offset": 0, 7 | "previous": "/v1/Account//Media?offset=0&limit=20", 8 | "total_count": 44 9 | }, 10 | "objects": [{ 11 | "content_type": "image/gif", 12 | "media_id": "91f8f9ce-e236-474f-a2b8-f75d9c35988a", 13 | "media_url": "http://127.0.0.1:80/media/MAODZKMDFJMJU3MTEYNG/f734eeec-e59f-11e9-89dc-0242ac110003/91f8f9ce-e236-474f-a2b8-f75d9c35988a", 14 | "message_uuid": "f734eeec-e59f-11e9-89dc-0242ac110003", 15 | "size": 766424 16 | }, 17 | { 18 | "content_type": "image/jpeg", 19 | "media_id": "455a2f85-acd5-4bb7-9a6a-a7b7341cd592", 20 | "media_url": "http://127.0.0.1:80/media/MAODZKMDFJMJU3MTEYNG/f734eeec-e59f-11e9-89dc-0242ac110003/455a2f85-acd5-4bb7-9a6a-a7b7341cd592", 21 | "message_uuid": "f734eeec-e59f-11e9-89dc-0242ac110003", 22 | "size": 47343 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /tests/resources/fixtures/messageGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "95747262-3f08-11e7-8bc8-065f6a74a84a", 3 | "error_code": null, 4 | "from_number": "911231231230", 5 | "message_direction": "outbound", 6 | "message_state": "sent", 7 | "message_time": "2017-05-22 21:36:14+05:30", 8 | "message_type": "sms", 9 | "message_uuid": "5b40a428-bfc7-4daf-9d06-726c558bf3b8", 10 | "resource_uri": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/Message/5b40a428-bfc7-4daf-9d06-726c558bf3b8/", 11 | "to_number": "911231231231", 12 | "total_amount": "0.00250", 13 | "total_rate": "0.00250", 14 | "units": 1, 15 | "message_sent_time": "2024-08-21 18:28:49.244057+05:30", 16 | "message_updated_time": "2024-08-21 18:28:51.94772+05:30", 17 | "error_message": "" 18 | } 19 | -------------------------------------------------------------------------------- /tests/resources/fixtures/messageListMediaResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "035eeada-6df1-11e6-b608-06a72a185e87", 3 | "message_uuid": "message_uuid", 4 | "objects": [ 5 | { 6 | "content_type": "application/pdf", 7 | "media_id": "0178eb8a-461a-4fd1-bc37-13eebfdc0676", 8 | "media_url": "https://media.plivo.com/Account/{auth_id}/Message/24d742b9-9b12-4397-93a7-da496bc874d9/Media/0178eb8a-461a-4fd1-bc37-13eebfdc0676", 9 | "message_uuid": "24d742b9-9b12-4397-93a7-da496bc874d9", 10 | "size": 433994 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /tests/resources/fixtures/messageSendResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "948b53a2-3f08-11e7-b6f4-061564b78b75", 3 | "message": "message(s) queued", 4 | "message_uuid": [ 5 | "5b40a428-bfc7-4daf-9d06-726c558bf3b8" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /tests/resources/fixtures/multiPartyCallsAddParticipantResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "bfe372c0-b451-11ea-815a-1094bbeb5c2c", 3 | "calls": [ 4 | { 5 | "to": "917702643094", 6 | "from": "918888888888", 7 | "call_uuid": "0b7b4242-8ee8-4872-b447-81b4ce972eae" 8 | }, 9 | { 10 | "to": "919629342168", 11 | "from": "918888888888", 12 | "call_uuid": "67c882e3-833b-4eb8-bdbc-6efcb806938a" 13 | } 14 | ], 15 | "message": "add participant action initiated", 16 | "request_uuid": "8420cdb1-f1d8-44a4-8c2a-556d156f1ffa" 17 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/multiPartyCallsEndMpcResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "6cbdc5b1-9aa9-11ea-ad7f-0242ac110005", 3 | "message": "MPC: Voice kick member(s) succeeded", 4 | "request_uuid": "75fecf73-7f77-404f-adc6-7d4a73d9a40d" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/multiPartyCallsGetParticipantResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "07a25e47-c1e3-11ea-ae24-0242ac110004", 3 | "billed_amount": 0.00500, 4 | "billed_duration": 60, 5 | "call_uuid": "90de6710-9404-40d1-ba31-f26d2f7c533f", 6 | "coach_mode": null, 7 | "duration": 30, 8 | "end_mpc_on_exit": false, 9 | "exit_cause": "Participant Call Hangup", 10 | "exit_time": "2020-07-09 07:25:07+00:00", 11 | "hold": null, 12 | "join_time": "2020-07-09 07:24:37+00:00", 13 | "member_id": "49", 14 | "mpc_uuid": "18905d56-79c8-41d4-a840-25feff71070e", 15 | "mute": null, 16 | "resource_uri": "/v1/Account/MA123456789012345678/MultiPartyCall/uuid_18905d56-79c8-41d4-a840-25feff71070e/Participant/49/", 17 | "role": "customer", 18 | "start_mpc_on_enter": true 19 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/multiPartyCallsKickMpcParticipantResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "6cbdc5b1-9aa9-11ea-ad7f-0242ac110005", 3 | "message": "MPC: Voice kick member(s) succeeded", 4 | "request_uuid": "75fecf73-7f77-404f-adc6-7d4a73d9a40d" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/multiPartyCallsStartMpcResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "6cbdc5b1-9aa9-11ea-ad7f-0242ac110005", 3 | "message": "MPC: Voice started", 4 | "request_uuid": "75fecf73-7f77-404f-adc6-7d4a73d9a40d" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/multiPartyCallsStartParticipantRecordingResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "e05b5263-45dc-11eb-9014-0242ac110003", 3 | "message": "MPC: test_mpc_1 record started", 4 | "recording_id": "e06ac332-45dc-11eb-94fe-06dd7f581a50", 5 | "recording_url": "https://media.plivo.com/v1/Account/MAOTE1OWE0MDK0MTLHYW/Recording/e06ac332-45dc-11eb-94fe-06dd7f581a50.mp3" 6 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/multiPartyCallsStartPlayAudioResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "325a5d1c-4380-11ec-b094-0242ac11000f", 3 | "message": "play queued into MPC", 4 | "mpcMemberId": [ 5 | "48" 6 | ], 7 | "mpcName": "MyMPC" 8 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/multiPartyCallsStartRecordingResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "6cbdc5b1-9aa9-11ea-ad7f-0242ac110005", 3 | "message": "MPC: Voice started recording", 4 | "request_uuid": "75fecf73-7f77-404f-adc6-7d4a73d9a40d" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/multiPartyCallsUpdateMpcParticipantResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "6cbdc5b1-9aa9-11ea-ad7f-0242ac110005", 3 | "message": { 4 | "coach_mode": "MPC: %s coach_mode enable/disable succeeded", 5 | "mute": "MPC: %s mute/unmute member(s) succeeded" 6 | }, 7 | "request_uuid": "75fecf73-7f77-404f-adc6-7d4a73d9a40d" 8 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/numberCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "changed", 3 | "api_id": "5a9fcb68-582d-11e1-86da-6ff39efcb949" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/numberGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "added_on": "2014-02-14", 3 | "active": false, 4 | "alias": null, 5 | "api_id": "88625e5e-1c92-11e4-80aa-12313f048015", 6 | "application": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Application/29986316244302815/", 7 | "carrier": "Plivo", 8 | "monthly_rental_rate": "0.80000", 9 | "number": "17609915566", 10 | "number_type": "local", 11 | "city": "USA", 12 | "cnam_lookup": "enabled", 13 | "compliance_application_id": null, 14 | "compliance_status": null, 15 | "country": "United States", 16 | "mms_enabled": false, 17 | "mms_rate": "0.00000", 18 | "tendlc_campaign_id": null, 19 | "tendlc_registration_status": "UNREGISTERED", 20 | "toll_free_sms_verification": null, 21 | "renewal_date": "2014-03-14", 22 | "region": "California, UNITED STATES", 23 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Number/17609915566/", 24 | "sms_enabled": true, 25 | "sms_rate": "0.00000", 26 | "sub_account": null, 27 | "voice_enabled": true, 28 | "voice_rate": "0.00850" 29 | } 30 | -------------------------------------------------------------------------------- /tests/resources/fixtures/numberUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "changed", 3 | "api_id": "5a9fcb68-582d-11e1-86da-6ff39efcb949" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/numberpoolListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "21a04c42-d229-11e9-b61e-0242ac110007", 3 | "meta": { 4 | "limit": 20, 5 | "next": "", 6 | "offset": 0, 7 | "previous": "", 8 | "total_count": 1 9 | }, 10 | "objects": [{ 11 | "account_phone_number_resource": "/v1/Account/MAODZKMDFJMJU3MTEYNG/Number/15799140348/", 12 | "added_on": "2019-09-03T08:50:09.578928Z", 13 | "country_iso2": "CA", 14 | "number": "15799140348", 15 | "number_pool_uuid": "659c7f88-c819-46e2-8af4-2d8a84249099", 16 | "type": "fixed" 17 | }] 18 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/numberpoolResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_phone_number_resource": "/v1/Account/MAODZKMDFJMJU3MTEYNG/Number/15799140348/", 3 | "added_on": "2019-09-03T08:50:09.578928Z", 4 | "api_id": "81ca9d16-d229-11e9-b61e-0242ac110007", 5 | "country_iso2": "CA", 6 | "number": "15799140348", 7 | "numberpool_uuid": "659c7f88-c819-46e2-8af4-2d8a84249099", 8 | "type": "fixed" 9 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/phlosMemberMemberActionsResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "" 3 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/phlosMemberMemberActionsValidationResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "" 3 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/phlosMemberPhloGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "" 3 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/phlosMemberPhloGetValidationResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "" 3 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/phoneNumberCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "aa52882c-1c88-11e4-bd8a-12313f016a39", 3 | "message": "created", 4 | "numbers": [ 5 | { 6 | "number": "14154009186", 7 | "status": "Success" 8 | } 9 | ], 10 | "status": "fulfilled" 11 | } 12 | -------------------------------------------------------------------------------- /tests/resources/fixtures/phoneNumberListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "859428b0-1c88-11e4-a2d1-22000ac5040c", 3 | "meta": { 4 | "limit": 20, 5 | "next": null, 6 | "offset": 0, 7 | "previous": null, 8 | "total_count": 9 9 | }, 10 | "objects": [ 11 | { 12 | "country": "UNITED STATES", 13 | "lata": 722, 14 | "monthly_rental_rate": "0.80000", 15 | "number": "14154009186", 16 | "type": "fixed", 17 | "prefix": "415", 18 | "rate_center": "SNFC CNTRL", 19 | "region": "United States", 20 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/PhoneNumber/14154009186/", 21 | "restriction": null, 22 | "restriction_text": null, 23 | "setup_rate": "0.00000", 24 | "sms_enabled": true, 25 | "sms_rate": "0.00800", 26 | "voice_enabled": true, 27 | "voice_rate": "0.00500", 28 | "prerequisites": [{"proof_required": true, 29 | "proof_type": [ "Any" ], 30 | "scope": "country", 31 | "type": "address"}] 32 | }, 33 | { 34 | "country": "UNITED STATES", 35 | "lata": 722, 36 | "monthly_rental_rate": "0.80000", 37 | "number": "14154009187", 38 | "type": "fixed", 39 | "prefix": "415", 40 | "rate_center": "SNFC CNTRL", 41 | "region": "United States", 42 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/PhoneNumber/14154009187/", 43 | "restriction": null, 44 | "restriction_text": null, 45 | "setup_rate": "0.00000", 46 | "sms_enabled": true, 47 | "sms_rate": "0.00800", 48 | "voice_enabled": true, 49 | "voice_rate": "0.00500" 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackAddNumberResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_phone_number_resource": "/v1/Account//Number//", 3 | "added_on": "2019-10-09T11:24:35.085797Z", 4 | "api_id": "612982e8-0a87-11ea-b072-0242ac110007", 5 | "country_iso2": "CA", 6 | "number": "15799140336", 7 | "uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 8 | "number_pool_uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 9 | "number_pool": "/v1/Account/MAODZKMDFJMJU3MTEYNG/NumberPool/ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f/", 10 | "type": "fixed" 11 | } 12 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackAddTollfreeResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_phone_number_resource": "/v1/Account//Number//", 3 | "added_on": "2019-10-09T11:24:35.085797Z", 4 | "api_id": "612982e8-0a87-11ea-b072-0242ac110007", 5 | "country_iso2": "CA", 6 | "number": "15799140336", 7 | "uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 8 | "number_pool_uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 9 | "number_pool": "/v1/Account/MAODZKMDFJMJU3MTEYNG/NumberPool/ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f/", 10 | "type": "fixed" 11 | } 12 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackBuyAndNumberResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_phone_number_resource": "/v1/Account//Number//", 3 | "added_on": "2019-10-09T11:24:35.085797Z", 4 | "api_id": "612982e8-0a87-11ea-b072-0242ac110007", 5 | "country_iso2": "CA", 6 | "number": "14845071864", 7 | "uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 8 | "number_pool_uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 9 | "number_pool": "/v1/Account/MAODZKMDFJMJU3MTEYNG/NumberPool/ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f/", 10 | "type": "fixed" 11 | } 12 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackCountNumbersResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_phone_number_resource": "/v1/Account//Number//", 3 | "added_on": "2019-10-09T11:24:35.085797Z", 4 | "api_id": "612982e8-0a87-11ea-b072-0242ac110007", 5 | "country_iso2": "CA", 6 | "uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 7 | "number_pool_uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 8 | "number_pool": "/v1/Account/MAODZKMDFJMJU3MTEYNG/NumberPool/ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f/", 9 | "api_id": "0dacbefa-0a87-11ea-b072-0242ac110007", 10 | "meta": { 11 | "limit": 20, 12 | "next": "", 13 | "offset": 0, 14 | "previous": "", 15 | "total_count": 6 16 | }, 17 | "objects": [ 18 | { 19 | "account_phone_number_resource": "/v1/Account/{auth_id}/Number/{your_number}/", 20 | "added_on": "2019-10-09T11:24:35.085797Z", 21 | "country_iso2": "US", 22 | "number": "{your_number}", 23 | "number_pool_uuid": "{number_pool_uuid}", 24 | "type": "fixed" 25 | }, 26 | { 27 | "account_phone_number_resource": "/v1/Account/{auth_id}/Number/{your_number}/", 28 | "added_on": "2019-10-09T11:24:35.085797Z", 29 | "country_iso2": "US", 30 | "number": "{your_number}", 31 | "number_pool_uuid": "{number_pool_uuid}", 32 | "type": "fixed" 33 | }, 34 | { 35 | "account_phone_number_resource": "/v1/Account/{auth_id}/Number/{your_number}/", 36 | "added_on": "2019-10-09T11:24:35.085797Z", 37 | "country_iso2": "CA", 38 | "number": "{your_number}", 39 | "number_pool_uuid": "{number_pool_uuid}", 40 | "type": "fixed" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackCreatePowerpackResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "4b2c83c4-09ff-11ea-b072-0242ac110007", 3 | "application_id": "20342783288007824", 4 | "application_type": "XML", 5 | "created_on": "2019-10-09T11:10:59.666461Z", 6 | "local_connect": true, 7 | "name": "test", 8 | "number_pool": "/v1/Account/{auth_id}/NumberPool//", 9 | "sticky_sender": true, 10 | "uuid": "" 11 | } 12 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackDeletePowerpackResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "dfsd-fsdfsd-345dfsd-fsdfs-sdfsd", 3 | "name": "My Powerpack", 4 | "smart_sender": true, 5 | "local_connect": false, 6 | "application_type": "XML", 7 | "application_id": 123912833468, 8 | "number_pool": "/v1/Account/{auth_id}/NumberPool//", 9 | "created_on": "2019-07-15T12:22:00Z" 10 | } 11 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "964edb6e-3f08-11e7-920b-0600a1193e9b", 3 | "response": "success" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackFindShortcodeResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "614b2776-0a88-11ea-b072-0242ac110007", 3 | "number_pool": "/v1/Account/{auth_id}/NumberPool//", 4 | "uuid": "dfsd-fsdfsd-345dfsd-fsdfs-sdfsd", 5 | "meta": { 6 | "limit": 20, 7 | "next": "", 8 | "offset": 0, 9 | "previous": "", 10 | "total_count": 1 11 | }, 12 | "objects": [ 13 | { 14 | "added_on": "2019-10-09T11:10:59.741978Z", 15 | "country_iso2": "US", 16 | "number_pool_uuid": "{number_pool_uuid}", 17 | "shortcode": "444444" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackFindTollfreeResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "614b2776-0a88-11ea-b072-0242ac110007", 3 | "number_pool": "/v1/Account/{auth_id}/NumberPool//", 4 | "uuid": "dfsd-fsdfsd-345dfsd-fsdfs-sdfsd", 5 | "meta": { 6 | "limit": 20, 7 | "next": "", 8 | "offset": 0, 9 | "previous": "", 10 | "total_count": 1 11 | }, 12 | "objects": [ 13 | { 14 | "added_on": "2019-10-09T11:10:59.741978Z", 15 | "country_iso2": "US", 16 | "number_pool_uuid": "{number_pool_uuid}", 17 | "tollfree": "18772209942" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackGetPowerpackResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "5ec4c8c9-cd74-42b5-9e41-0d7670d6bb46", 3 | "name": "My Powerpack", 4 | "smart_sender": true, 5 | "local_connect": false, 6 | "application_type": "XML", 7 | "application_id": 123912833468, 8 | "number_pool": "/v1/Account/{auth_id}/NumberPool//", 9 | "created_on": "2019-07-15T12:22:00Z" 10 | } 11 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackListNumbersResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "0dacbefa-0a87-11ea-b072-0242ac110007", 3 | "uuid": "d35f2e82-d387-427f-8594-6fa07613c43a", 4 | "number_pool": "/v1/Account/{auth_id}/NumberPool/d35f2e82-d387-427f-8594-6fa07613c43a/", 5 | "meta": { 6 | "limit": 20, 7 | "next": "", 8 | "offset": 0, 9 | "previous": "", 10 | "total_count": 3 11 | }, 12 | "objects": [ 13 | { 14 | "account_phone_number_resource": "/v1/Account/{auth_id}/Number/{your_number}/", 15 | "added_on": "2019-10-09T11:24:35.085797Z", 16 | "country_iso2": "US", 17 | "number": "{your_number}", 18 | "number_pool_uuid": "d35f2e82-d387-427f-8594-6fa07613c43a", 19 | "type": "fixed" 20 | }, 21 | { 22 | "account_phone_number_resource": "/v1/Account/{auth_id}/Number/{your_number}/", 23 | "added_on": "2019-10-09T11:24:35.085797Z", 24 | "country_iso2": "US", 25 | "number": "{your_number}", 26 | "number_pool_uuid": "d35f2e82-d387-427f-8594-6fa07613c43a", 27 | "type": "fixed" 28 | }, 29 | { 30 | "account_phone_number_resource": "/v1/Account/{auth_id}/Number/{your_number}/", 31 | "added_on": "2019-10-09T11:24:35.085797Z", 32 | "country_iso2": "CA", 33 | "number": "{your_number}", 34 | "number_pool_uuid": "d35f2e82-d387-427f-8594-6fa07613c43a", 35 | "type": "fixed" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackListPowerpackResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "e44c159e-0a02-11ea-b072-0242ac110007", 3 | "meta": { 4 | "limit": 20, 5 | "next": "/api/v1/account/xxxxxxx/Powerpack?offset=20&limit=20", 6 | "offset": 0, 7 | "total_count": 53 8 | }, 9 | "objects": [ 10 | { 11 | "application_id": "", 12 | "application_type": "", 13 | "created_on": "2019-10-09T11:10:59.666461Z", 14 | "local_connect": true, 15 | "name": "test", 16 | "number_pool": "/v1/Account/xxxxxxxxx/NumberPool//", 17 | "sticky_sender": true, 18 | "uuid": "" 19 | }, 20 | { 21 | "application_id": "", 22 | "application_type": "", 23 | "created_on": "2019-10-09T17:03:31.837944Z", 24 | "local_connect": false, 25 | "name": "p23", 26 | "number_pool": "/v1/Account/xxxxxxxx/NumberPool//", 27 | "sticky_sender": false, 28 | "uuid": "" 29 | }, 30 | { 31 | "application_id": "", 32 | "application_type": "", 33 | "created_on": "2019-10-09T16:54:34.0117Z", 34 | "local_connect": false, 35 | "name": "p22", 36 | "number_pool": "/v1/Account/xxxxxxxx/NumberPool//", 37 | "sticky_sender": false, 38 | "uuid": "" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "ff25223a-1c9f-11e4-80aa-12313f048015", 3 | "meta": { 4 | "limit": 20, 5 | "next": null, 6 | "offset": 0, 7 | "previous": null, 8 | "total_count": 0 9 | }, 10 | "objects": [ 11 | { 12 | "application_id": "33660394121755210", 13 | "application_type": "XML", 14 | "created_on": "2019-09-03T08:50:09.510692Z", 15 | "local_connect": false, 16 | "name": "vishnu_sep_01", 17 | "number_pool": "/v1/Account/MAODZKMDFJMJU3MTEYNG/NumberPool/659c7f88-c819-46e2-8af4-2d8a84249099/", 18 | "sticky_sender": true, 19 | "uuid": "86bbb125-97bb-4d72-89fd-81d5c515b015" 20 | }, 21 | { 22 | "application_id": "", 23 | "application_type": "", 24 | "created_on": "2019-09-03T18:35:19.548601Z", 25 | "local_connect": true, 26 | "name": "Neel_ppk_sdk-14", 27 | "number_pool": "/v1/Account/MAODZKMDFJMJU3MTEYNG/NumberPool/13611cea-d86d-4da9-b70a-37f47478b5c0/", 28 | "sticky_sender": true, 29 | "uuid": "70f8c51f-2700-4067-9101-f177a98ec65d" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackListShortcodeResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "614b2776-0a88-11ea-b072-0242ac110007", 3 | "uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 4 | "number_pool": "/v1/Account/xxxxxxxxx/NumberPool/ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f/", 5 | "meta": { 6 | "limit": 20, 7 | "offset": 0, 8 | "next": "", 9 | "previous": "", 10 | "total_count": 1 11 | }, 12 | "objects": [ 13 | { 14 | "added_on": "2019-10-09T11:10:59.741978Z", 15 | "country_iso2": "US", 16 | "number_pool_uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 17 | "shortcode": "{your_shortcode}" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackListTollfreeResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "614b2776-0a88-11ea-b072-0242ac110007", 3 | "uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 4 | "number_pool": "/v1/Account/xxxxxxxxx/NumberPool/ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f/", 5 | "meta": { 6 | "limit": 20, 7 | "offset": 0, 8 | "next": "", 9 | "previous": "", 10 | "total_count": 1 11 | }, 12 | "objects": [ 13 | { 14 | "added_on": "2019-10-09T11:10:59.741978Z", 15 | "country_iso2": "US", 16 | "number_pool_uuid": "ca5fd1f2-26c0-43e9-a7e4-0dc426e9dd2f", 17 | "tollfree": "{your_tollfree}" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackRemoveNumberResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "d35f2e82-d387-427f-8594-6fa07613c43a", 3 | "name": "My Powerpack", 4 | "smart_sender": true, 5 | "local_connect": false, 6 | "application_type": "XML", 7 | "application_id": 123912833468, 8 | "number_pool": "/v1/Account/{auth_id}/NumberPool//", 9 | "created_on": "2019-07-15T12:22:00Z" 10 | } 11 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackRemoveShortcodeResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "d35f2e82-d387-427f-8594-6fa07613c43a", 3 | "name": "My Powerpack", 4 | "smart_sender": true, 5 | "local_connect": false, 6 | "application_type": "XML", 7 | "application_id": 123912833468, 8 | "number_pool": "/v1/Account/{auth_id}/NumberPool//", 9 | "created_on": "2019-07-15T12:22:00Z" 10 | } 11 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackRemoveTollfreeResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "d35f2e82-d387-427f-8594-6fa07613c43a", 3 | "name": "My Powerpack", 4 | "smart_sender": true, 5 | "local_connect": false, 6 | "application_type": "XML", 7 | "application_id": 123912833468, 8 | "number_pool": "/v1/Account/{auth_id}/NumberPool//", 9 | "created_on": "2019-07-15T12:22:00Z" 10 | } 11 | -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "b2e24102-d228-11e9-b61e-0242ac110007", 3 | "application_id": "33660394121755210", 4 | "application_type": "XML", 5 | "created_on": "2019-09-03T08:50:09.510692Z", 6 | "local_connect": false, 7 | "name": "vishnu_sep_01", 8 | "number_pool": "/v1/Account/MAODZKMDFJMJU3MTEYNG/NumberPool/659c7f88-c819-46e2-8af4-2d8a84249099/", 9 | "sticky_sender": true, 10 | "uuid": "86bbb125-97bb-4d72-89fd-81d5c515b015" 11 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/powerpackUpdatePowerpackResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "4b2c83c4-09ff-11ea-b072-0242ac110007", 3 | "uuid": "d35f2e82-d387-427f-8594-6fa07613c43a", 4 | "application_id": "20342783288007824", 5 | "application_type": "XML", 6 | "created_on": "2019-10-09T11:10:59.666461Z", 7 | "local_connect": true, 8 | "name": "test", 9 | "number_pool": "/v1/Account/{auth_id}/NumberPool/d35f2e82-d387-427f-8594-6fa07613c43a/", 10 | "sticky_sender": true, 11 | "uuid": "" 12 | } 13 | -------------------------------------------------------------------------------- /tests/resources/fixtures/pricingGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "25b3d816-1c9f-11e4-bd8a-12313f016a39", 3 | "country": "United States", 4 | "country_code": 1, 5 | "country_iso": "US", 6 | "message": { 7 | "inbound": { 8 | "rate": "0.00000" 9 | }, 10 | "outbound": { 11 | "rate": "0.00650" 12 | }, 13 | "outbound_networks_list": [ 14 | { 15 | "group_name": "US", 16 | "rate": "0.00650" 17 | }, 18 | { 19 | "group_name": "US", 20 | "rate": "0.00650" 21 | } 22 | ] 23 | }, 24 | "phone_numbers": { 25 | "local": { 26 | "rate": "0.80000" 27 | }, 28 | "tollfree": { 29 | "rate": "1.00000" 30 | } 31 | }, 32 | "voice": { 33 | "inbound": { 34 | "ip": { 35 | "rate": "0.00300" 36 | }, 37 | "local": { 38 | "rate": "0.00850" 39 | }, 40 | "tollfree": { 41 | "rate": "0.02100" 42 | } 43 | }, 44 | "outbound": { 45 | "ip": { 46 | "rate": "0.00300" 47 | }, 48 | "local": { 49 | "rate": "0.01200" 50 | }, 51 | "rates": [ 52 | { 53 | "prefix": [ 54 | "1" 55 | ], 56 | "rate": "0.01200" 57 | }, 58 | { 59 | "prefix": [ 60 | "1340" 61 | ], 62 | "rate": "0.02400" 63 | }, 64 | { 65 | "prefix": [ 66 | "1808" 67 | ], 68 | "rate": "0.03400" 69 | }, 70 | { 71 | "prefix": [ 72 | "1907" 73 | ], 74 | "rate": "0.17900" 75 | }, 76 | { 77 | "prefix": [ 78 | "1900" 79 | ], 80 | "rate": "0.60300" 81 | } 82 | ], 83 | "tollfree": { 84 | "rate": "0.00300" 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/resources/fixtures/profileCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "99ab47ae-b01c-11ec-bc21-0242ac110002", 3 | "profile_uuid": "43d0616e-d50a-445a-a84e-310a089f0618" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/profileGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "63287a98-b018-11ec-bc21-0242ac110002", 3 | "profile": { 4 | "alt_business_id_type": "NONE", 5 | "authorized_contact": { 6 | "name": " " 7 | }, 8 | "company_name": "ABC Inc.", 9 | "customer_type": "RESELLER", 10 | "ein": "111111111", 11 | "ein_issuing_country": "US", 12 | "entity_type": "PUBLIC_PROFIT", 13 | "profile_alias": "vishnu1", 14 | "profile_type": "SECONDARY", 15 | "profile_uuid": "201faedc-7df9-4840-9ab1-3997ce3f7cf4", 16 | "stock_symbol": "ABC", 17 | "vertical": "ENERGY" 18 | } 19 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/profileUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "fd1fdade-17cd-11ed-b25b-0242ac110004", 3 | "profile": { 4 | "address": { 5 | "city": "New York", 6 | "country": "IN", 7 | "postal_code": "10001", 8 | "state": "NY", 9 | "street": "123" 10 | }, 11 | "alt_business_id_type": "NONE", 12 | "authorized_contact": { 13 | "email": "test@plivo.com", 14 | "first_name": "John", 15 | "last_name": "Doe", 16 | "phone": "919539113734", 17 | "seniority": "admin", 18 | "title": "Doe" 19 | }, 20 | "company_name": "ABC Inc.", 21 | "customer_type": "DIRECT", 22 | "ein": "123456789", 23 | "ein_issuing_country": "US", 24 | "entity_type": "PRIVATE", 25 | "primary_profile": "0b8a783c-9906-4c84-8ace-cb88413a1da6", 26 | "profile_alias": "used_in_automation_01", 27 | "profile_type": "SECONDARY", 28 | "profile_uuid": "09322f43-fe16-4525-b8e4-4229c867795d", 29 | "vertical": "ENERGY", 30 | "website": "www.google.com" 31 | } 32 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/recordingGetAddedFilterResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_time":"2022-04-12 16:08:53.523657+05:30", 3 | "call_uuid":"f72eea2a-446b-4412-a17f-3b17083bd25a", 4 | "conference_name":"", 5 | "from_number":"+919768368717", 6 | "monthly_recording_storage_amount": 0.0008, 7 | "recording_duration_ms":"7560.00000", 8 | "recording_end_ms":"1649759930398.00000", 9 | "recording_format":"mp3", 10 | "recording_id":"d405c4eb-d562-4399-af32-6ff3c57fa55x", 11 | "recording_start_ms":"1649759922838.00000", 12 | "recording_storage_duration": 209, 13 | "recording_storage_rate": 0.0004, 14 | "recording_type":"call", 15 | "recording_url":"https://media.plivo.com/v1/Account/MAZTCXYJFKZJK3N2Q3YT/Recording/d405c4eb-d562-4399-af32-6ff3c57fa559.mp3", 16 | "resource_uri":"/v1/Account/MAZTCXYJFKZJK3N2Q3YT/Recording/d405c4eb-d562-4399-af32-6ff3c57fa559/", 17 | "to_number":"sip:ajay6121801985815245533110@phone.plivo.com", 18 | "rounded_recording_duration": 120 19 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/recordingGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_time": "2014-08-05 16:15:15.852059+05:30", 3 | "api_id": "7abf0744-1ca0-11e4-a2d1-22000ac5040c", 4 | "call_uuid": "c2c128e2-1c8c-11e4-9bff-1db8a9db0432", 5 | "conference_name": "noname", 6 | "monthly_recording_storage_amount": 0.0008, 7 | "recording_duration_ms": "345100.00000", 8 | "recording_end_ms": "1407235509007.00000", 9 | "recording_format": "mp3", 10 | "recording_id": "c2186400-1c8c-11e4-a664-0026b945b52x", 11 | "recording_start_ms": "1407235163907.00000", 12 | "recording_storage_duration": 209, 13 | "recording_storage_rate": 0.0004, 14 | "recording_type": "conference", 15 | "recording_url": "http://s3.amazonaws.com/recordings_2013/c2186400-1c8c-11e4-a664-0026b945b52x.mp3", 16 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Recording/c2186400-1c8c-11e4-a664-0026b945b52x/", 17 | "rounded_recording_duration": 120 18 | } 19 | -------------------------------------------------------------------------------- /tests/resources/fixtures/recordingListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "ff25223a-1c9f-11e4-80aa-12313f048015", 3 | "meta": { 4 | "limit": 3, 5 | "next": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Recording/?limit=3&offset=3", 6 | "offset": 0, 7 | "previous": null, 8 | "total_count": 948 9 | }, 10 | "objects": [ 11 | { 12 | "add_time": "2014-08-05 16:15:15.852059+05:30", 13 | "call_uuid": "c2c128e2-1c8c-11e4-9bff-1db8a9db0432", 14 | "conference_name": "noname", 15 | "monthly_recording_storage_amount": 0.0008, 16 | "recording_duration_ms": "345100.00000", 17 | "recording_end_ms": "1407235509007.00000", 18 | "recording_format": "mp3", 19 | "recording_id": "c2186400-1c8c-1124-a664-0026b945b522", 20 | "recording_start_ms": "1407235163907.00000", 21 | "recording_storage_duration": 209, 22 | "recording_storage_rate": 0.0004, 23 | "recording_type": "conference", 24 | "recording_url": "http://s3.amazonaws.com/recordings_2013/c2186400-1c8c-1124-a664-0026b945b522.mp3", 25 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Recording/c2186400-1c8c-1124-a664-0026b945b522/", 26 | "rounded_recording_duration": 120 27 | }, 28 | { 29 | "add_time": "2014-08-05 16:05:21.993853+05:30", 30 | "call_uuid": "fc773e88-1c8b-11e4-b25a-0fe7bcc54670", 31 | "conference_name": "noname", 32 | "monthly_recording_storage_amount": 0.0008, 33 | "recording_duration_ms": "90700.00000", 34 | "recording_end_ms": "1407234920253.00000", 35 | "recording_format": "mp3", 36 | "recording_id": "fc2716b0-1c8b-11e4-bwad-842b2b17453e", 37 | "recording_start_ms": "1407234829553.00000", 38 | "recording_storage_duration": 209, 39 | "recording_storage_rate": 0.0004, 40 | "recording_type": "conference", 41 | "recording_url": "http://s3.amazonaws.com/recordings_2013/fc2716b0-1c8b-11e4-bwad-842b2b17453e.mp3", 42 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Recording/fc2716b0-1c8b-11e4-bwad-842b2b17453e/", 43 | "rounded_recording_duration": 120 44 | }, 45 | { 46 | "add_time": "2014-08-05 15:51:56.582492+05:30", 47 | "call_uuid": "3eb4c16e-1c8a-11e4-978e-0fe7bcc54670", 48 | "conference_name": "noname", 49 | "monthly_recording_storage_amount": 0.0008, 50 | "recording_duration_ms": "34100.00000", 51 | "recording_end_ms": "1407234115543.00000", 52 | "recording_format": "mp3", 53 | "recording_id": "3e701c9e-1c8a-11e4-bwad-842b2b17453e", 54 | "recording_start_ms": "1407234081443.00000", 55 | "recording_storage_duration": 209, 56 | "recording_storage_rate": 0.0004, 57 | "recording_type": "conference", 58 | "recording_url": "http://s3.amazonaws.com/recordings_2013/3e701c9e-1c8a-11e4-bwad-842b2b17453e.mp3", 59 | "resource_uri": "/v1/Account/MANWVLYTK4ZWU1YTY4ZT/Recording/3e701c9e-1c8a-11e4-bwad-842b2b17453e/", 60 | "rounded_recording_duration": 120 61 | } 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /tests/resources/fixtures/sessionGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "8faf830c-4be8-485a-9235-045495b03148", 3 | "session_uuid": "5c533d15-1975-4f20-80be-40174142ea37", 4 | "app_uuid": "eeb90c7b-9367-4457-8644-47007661bff5", 5 | "recipient": "918681951370", 6 | "channel": "sms", 7 | "status": "expired", 8 | "count": 1, 9 | "attempt_details": [ 10 | { 11 | "channel": "sms", 12 | "attempt_uuid": "c1ed6b11-783d-4efd-8b45-d372e02d2943", 13 | "status": "failed", 14 | "time": "2023-07-18T08:46:14.865388Z" 15 | } 16 | ], 17 | "charges": { 18 | "total_charge": "0", 19 | "validation_charge": "0.0000", 20 | "attempt_charges": [ 21 | { 22 | "attempt_uuid": "c1ed6b11-783d-4efd-8b45-d372e02d2943", 23 | "channel": "sms", 24 | "charge": "0.00000" 25 | } 26 | ] 27 | }, 28 | "created_at": "2023-07-18T08:46:14.851205Z", 29 | "updated_at": "2023-07-18T08:46:14.865388Z" 30 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/shortcodeListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "cd5fde12-d229-11e9-b61e-0242ac110007", 3 | "meta": { 4 | "limit": 20, 5 | "next": "", 6 | "offset": 0, 7 | "previous": "", 8 | "total_count": 1 9 | }, 10 | "objects": [{ 11 | "added_on": "2019-09-03T08:50:09.578928Z", 12 | "country_iso2": "CA", 13 | "number_pool_uuid": "659c7f88-c819-46e2-8af4-2d8a84249099", 14 | "shortcode": "444444" 15 | }] 16 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/shortcodeResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "added_on": "2019-09-03T08:50:09.578928Z", 3 | "country_iso2": "CA", 4 | "number_pool_uuid": "659c7f88-c819-46e2-8af4-2d8a84249099", 5 | "shortcode": "444444" 6 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/subaccountCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "97c8d1de-3f08-11e7-b6f4-061564b78b75", 3 | "auth_id": "SANDLHYZBIZMU4ZDEXNM", 4 | "auth_token": "MTMzZTZjNzdiNDVmY2VhZDQyNTUwYWVjNzI2OThk", 5 | "message": "created" 6 | } 7 | -------------------------------------------------------------------------------- /tests/resources/fixtures/subaccountGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "account": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/", 3 | "api_id": "98b2d7ca-3f08-11e7-b886-067c5485c240", 4 | "auth_id": "SAODNKNDDMY2EXY2JKMG", 5 | "auth_token": "YjE4NWEyNzgzODkyNGI2NjdmZmZhZTdjNzlkYTE1", 6 | "created": "2017-05-22", 7 | "enabled": false, 8 | "modified": null, 9 | "name": "0.7542345556290114", 10 | "new_auth_token": "YjE4NWEyNzgzODkyNGI2NjdmZmZhZTdjNzlkYTE1", 11 | "resource_uri": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/Subaccount/SAODNKNDDMY2EXY2JKMG/" 12 | } 13 | -------------------------------------------------------------------------------- /tests/resources/fixtures/subaccountListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "98389d2a-3f08-11e7-b886-067c5485c240", 3 | "meta": { 4 | "limit": 20, 5 | "next": null, 6 | "offset": 0, 7 | "previous": null, 8 | "total_count": 3 9 | }, 10 | "objects": [ 11 | { 12 | "account": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/", 13 | "auth_id": "SAODNKNDDMY2EXY2JKMG", 14 | "auth_token": "YjE4NWEyNzgzODkyNGI2NjdmZmZhZTdjNzlkYTE1", 15 | "created": "2017-05-22", 16 | "enabled": false, 17 | "modified": null, 18 | "name": "0.7542345556290114", 19 | "new_auth_token": "YjE4NWEyNzgzODkyNGI2NjdmZmZhZTdjNzlkYTE1", 20 | "resource_uri": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/Subaccount/SAODNKNDDMY2EXY2JKMG/" 21 | }, 22 | { 23 | "account": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/", 24 | "auth_id": "SANDBKY2YXMDU3MDLJNT", 25 | "auth_token": "OWVjODk1NzFjNzY1MTJiNDVkNTlmMGUyNjMyNWRh", 26 | "created": "2017-05-22", 27 | "enabled": false, 28 | "modified": null, 29 | "name": "0.11472037419916115", 30 | "new_auth_token": "OWVjODk1NzFjNzY1MTJiNDVkNTlmMGUyNjMyNWRh", 31 | "resource_uri": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/Subaccount/SANDBKY2YXMDU3MDLJNT/" 32 | }, 33 | { 34 | "account": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/", 35 | "auth_id": "SANDLHYZBIZMU4ZDEXNM", 36 | "auth_token": "MTMzZTZjNzdiNDVmY2VhZDQyNTUwYWVjNzI2OThk", 37 | "created": "2017-05-22", 38 | "enabled": false, 39 | "modified": null, 40 | "name": "0.0636850567965852", 41 | "new_auth_token": "MTMzZTZjNzdiNDVmY2VhZDQyNTUwYWVjNzI2OThk", 42 | "resource_uri": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/Subaccount/SANDLHYZBIZMU4ZDEXNM/" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /tests/resources/fixtures/subaccountUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "992d65c6-3f08-11e7-b886-067c5485c240", 3 | "message": "changed" 4 | } 5 | -------------------------------------------------------------------------------- /tests/resources/fixtures/tokenCreateResponse.json: -------------------------------------------------------------------------------- 1 | {"api_id": "5cbad7b4-19f4-11ed-8b03-0242ac110005", 2 | "token": "eyJhbGciOiJIUzI1NiIsImN0eSI6InBsaXZvO3Y9MSIsInR5cCI6IkpXVCJ9.eyJhcHAiOiIiLCJleHAiOjE2NjAzNjM2ODMsImlzcyI6Ik1BTURWTFpKWTJaR1k1TVdVMVpKIiwibmJmIjoxNjYwMjc3MjgzLCJwZXIiOnsidm9pY2UiOnsiaW5jb21pbmdfYWxsb3ciOmZhbHNlLCJvdXRnb2luZ19hbGxvdyI6dHJ1ZX19LCJzdWIiOiIifQ.LAwFEuotTmbZeGWBhfNT4X2KbRapYF23BrkwVfmr5A4" 3 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/tollfreeVerificationCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "5a9fcb68-582d-11e1-86da-6ff39efcb949", 3 | "message": "Tollfree verification request for 18339404937 submitted succesfully", 4 | "uuid": "f19c4773-4ae6-4b75-92ea-9cf3ea4227d6" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/tollfreeVerificationGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "4e1f954c-baf3-11ec-bafe-0242ac110003", 3 | "additional_information": "", 4 | "callback_method": "POST", 5 | "callback_url": "http://plivobin-prod-usw1.plivops.com/1e4jvpt1", 6 | "created": "2023-10-12T15:28:10.216507Z", 7 | "extra_data": "", 8 | "message_sample": "1.Your Plivo verification code is xxxx. 2.Dear , you have signed up on Plivo successfully.", 9 | "number": "18449605287", 10 | "optin_image_url": "https://www.plivo.com", 11 | "optin_type": "MOBILE_QR_CODE", 12 | "profile_uuid": "0ead82e0-fc80-4dc3-888b-7778932cf134", 13 | "error_message": "Demo Testing Sanity", 14 | "status": "REJECTED", 15 | "last_modified": "2023-10-13T08:24:26.619565Z", 16 | "usecase": "2FA", 17 | "usecase_summary": "1.We send 2FA codes to customers when they signup. 2.Dear , you have signed up on Plivo successfully.", 18 | "uuid": "312b3119-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 19 | "volume": "100" 20 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/tollfreeVerificationListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "4e15f360-72fe-11ee-8538-e20bbbfb283e", 3 | "meta": { 4 | "count": 2, 5 | "limit": 10, 6 | "next": null, 7 | "offset": 10, 8 | "previous": "/v1/Account/MAXXXXXXXXXXXXXXXXXX/TollfreeVerification/?limit=10&offset=0" 9 | }, 10 | "objects": [ 11 | { 12 | "additional_information": "", 13 | "callback_method": "POST", 14 | "callback_url": "http://plivobin-prod-usw1.plivops.com/1e4jvpt1", 15 | "created": "2023-10-12T15:28:10.216507Z", 16 | "extra_data": "", 17 | "message_sample": "1.Your Plivo verification code is xxxx. 2.Dear , you have signed up on Plivo successfully.", 18 | "number": "18449605287", 19 | "optin_image_url": "https://www.plivo.com", 20 | "optin_type": "MOBILE_QR_CODE", 21 | "profile_uuid": "0ead82e0-fc80-4dc3-888b-7778932cf134", 22 | "error_message": "Demo Testing Sanity", 23 | "status": "REJECTED", 24 | "last_modified": "2023-10-13T08:24:26.619565Z", 25 | "usecase": "2FA", 26 | "usecase_summary": "1.We send 2FA codes to customers when they signup. 2.Dear , you have signed up on Plivo successfully.", 27 | "uuid": "312b3119-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 28 | "volume": "100" 29 | }, 30 | { 31 | "additional_information": "", 32 | "callback_method": "POST", 33 | "callback_url": "http://plivobin-prod-usw1.plivops.com/1e4jvpt1", 34 | "created": "2023-10-12T12:06:46.527653Z", 35 | "extra_data": "", 36 | "message_sample": "1.Your Plivo verification code is xxxx. 2.Dear , you have signed up on Plivo successfully.", 37 | "number": "18449605287", 38 | "optin_image_url": "https://www.plivo.com", 39 | "optin_type": "MOBILE_QR_CODE", 40 | "profile_uuid": "0ead82e0-fc80-4dc3-888b-7778932cf134", 41 | "error_message": "Demo Testing Sanity", 42 | "status": "REJECTED", 43 | "last_modified": "2023-10-12T15:27:56.052433Z", 44 | "usecase": "2FA", 45 | "usecase_summary": "1.We send 2FA codes to customers when they signup. 2.Dear , you have signed up on Plivo successfully.", 46 | "uuid": "96ec880d-b7ee-4eb5-7c9a-3ff28826e77c", 47 | "volume": "100" 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/tollfreeVerificationUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "4e1f954c-baf3-11ec-bafe-0242ac110003", 3 | "message": "Tollfree verification request for 18339404937 updated succesfully" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/transcriptionCreateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "c9db3f38-55e8-4d97-93b2-1ece5a342528", 3 | "message": "transcription in progress" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/transcriptionDeleteResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "47ab0f0d-a7db-4f56-8200-6555cd3194e8", 3 | "message": "request accepted" 4 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/transcriptionGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "f83b30a3-228c-4676-813e-985fcdf61201", 3 | "cost": 0.05, 4 | "rate": 0.05, 5 | "recording_duration_ms": 22780, 6 | "recording_start_ms": 1729761222174, 7 | "status": "success", 8 | "transcription": "\nSpeaker 0: Scan just to be safe. If you notice any error messages, please let me know immediately. They can help us diagnose the issue better. If it continues to freeze, we might need to look into your system performance. I can guide you through checking your task manager if that helps.\n\nSometimes, background processes can use up a lot of resources. I under" 9 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/verifyCalleridGetResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "alias": "test", 3 | "api_id": "ee7c3cb1-c921-42c9-832b-950fb8344b9b", 4 | "country": "IN", 5 | "created_at": "2023-09-22T14:11:03.091534Z", 6 | "modified_at": "2023-09-22T14:11:03.091534Z", 7 | "phone_number": "+919768368718", 8 | "subaccount": "", 9 | "verification_uuid": "0f978b20-9e2b-4cfe-99fe-f7087c03b8e1" 10 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/verifyCalleridInitiateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "2a5e81d7-deb3-46cd-ac34-c05ca9139b6f", 3 | "message": "Verification code is sent to number +919768368717 which is valid for 15 minutes", 4 | "verification_uuid": "407796a6-1f3e-4607-a9d9-5a5376b59a7b" 5 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/verifyCalleridListResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_id": "3b21c7d7-09f7-489b-b753-659814a676bf", 3 | "meta": { 4 | "limit": 20, 5 | "next": null, 6 | "offset": 0, 7 | "previous": null, 8 | "total_count": 4 9 | }, 10 | "objects": [ 11 | { 12 | "alias": "abhishek", 13 | "country": "IN", 14 | "created_at": "2023-09-25T14:40:38.68729Z", 15 | "modified_at": "2023-09-25T14:40:38.68729Z", 16 | "phone_number": "+919653244280", 17 | "resource_uri": "/v1/Account/MADCHANDRESH02TANK06/VerifiedCallerId/919653244280", 18 | "subaccount": "", 19 | "verification_uuid": "01be6b07-b106-46e2-8dfc-096d8e22cc0e" 20 | }, 21 | { 22 | "alias": "abhishek", 23 | "country": "IN", 24 | "created_at": "2023-09-25T13:10:39.968133Z", 25 | "modified_at": "2023-09-25T13:10:39.968133Z", 26 | "phone_number": "+919768368717", 27 | "resource_uri": "/v1/Account/MADCHANDRESH02TANK06/VerifiedCallerId/919768368717", 28 | "subaccount": "", 29 | "verification_uuid": "2e68eb73-4d54-4391-bc98-71cd380911a4" 30 | }, 31 | { 32 | "alias": "test", 33 | "country": "IN", 34 | "created_at": "2023-09-22T14:11:03.091534Z", 35 | "modified_at": "2023-09-22T14:11:03.091534Z", 36 | "phone_number": "+919768368718", 37 | "resource_uri": "/v1/Account/MADCHANDRESH02TANK06/VerifiedCallerId/919768368718", 38 | "subaccount": "", 39 | "verification_uuid": "0f978b20-9e2b-4cfe-99fe-f7087c03b8e1" 40 | }, 41 | { 42 | "alias": "Test2", 43 | "country": "", 44 | "created_at": "2023-08-30T07:47:43.87171Z", 45 | "modified_at": "2023-08-30T07:47:43.87171Z", 46 | "phone_number": "+917691021365", 47 | "resource_uri": "/v1/Account/MADCHANDRESH02TANK06/VerifiedCallerId/917691021365", 48 | "subaccount": "SAMTU0Y2FKNGETYZDKNI", 49 | "verification_uuid": "20265c57-2d8e-46fe-8fa8-e8ab4ee58a8c" 50 | } 51 | ] 52 | } -------------------------------------------------------------------------------- /tests/resources/fixtures/verifyCalleridUpdateResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "alias": "testAbhishek", 3 | "api_id": "6b143cb3-9c8a-42ad-b6b7-412ebd5c60a9", 4 | "country": "IN", 5 | "created_at": "2023-09-22T14:11:03.091534Z", 6 | "modified_at": "2023-09-22T14:11:03.091534Z", 7 | "phone_number": "+919768368718", 8 | "subaccount": "SAMTU0Y2FKNGETYZDKNI", 9 | "verification_uuid": "0f978b20-9e2b-4cfe-99fe-f7087c03b8e1" 10 | } 11 | -------------------------------------------------------------------------------- /tests/resources/fixtures/verifyCalleridVerifyResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "alias": "abhishek", 3 | "api_id": "584a97aa-ca1d-44f2-9dd3-e58fbacd6649", 4 | "channel": "call", 5 | "country": "IN", 6 | "created_at": "2023-09-25T13:10:39.968133341Z", 7 | "phone_number": "+919768368717", 8 | "verification_uuid": "2e68eb73-4d54-4391-bc98-71cd380911a4" 9 | } -------------------------------------------------------------------------------- /tests/resources/test_brand.py: -------------------------------------------------------------------------------- 1 | from plivo import exceptions 2 | from tests.base import PlivoResourceTestCase 3 | from tests.decorators import with_response 4 | 5 | class BrandTest(PlivoResourceTestCase): 6 | @with_response(200) 7 | def test_create(self): 8 | response = self.client.brand.create( 9 | brand_alias = "brand name sample", 10 | brand_type="STARTER", 11 | profile_uuid="201faedc-7df9-4840-9ab1-3997ce3f7cf4", 12 | secondary_vetting=False, 13 | url="http://example.come/test", 14 | method="POST") 15 | self.assertEqual('POST', self.client.current_request.method) 16 | self.assertUrlEqual( 17 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/10dlc/Brand/', 18 | self.client.current_request.url) 19 | 20 | @with_response(200) 21 | def test_get(self): 22 | response = self.client.brand.get(brand_id='BRPXS6E') 23 | self.client.set_expected_response( 24 | status_code=202, data_to_return=response) 25 | # Verifying the endpoint hit 26 | self.assertUrlEqual( 27 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/10dlc/Brand/BRPXS6E/', 28 | self.client.current_request.url) 29 | 30 | # Verifying the method used 31 | self.assertEqual('GET', self.client.current_request.method) 32 | 33 | @with_response(200) 34 | def test_list(self): 35 | res = self.client.brand.list(limit=2, offset=0) 36 | # Test if ListResponseObject's __iter__ is working correctly 37 | self.assertGreater(len(list(res.brands)), 0) 38 | # Verifying the endpoint hit 39 | self.assertUrlEqual( 40 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/10dlc/Brand/?limit=2&offset=0', 41 | self.client.current_request.url) 42 | # Verifying the method used 43 | self.assertEqual('GET', self.client.current_request.method) 44 | 45 | @with_response(200) 46 | def test_get_usecases(self): 47 | response = self.client.brand.get_usecases(brand_id='BRPXS6E') 48 | self.client.set_expected_response( 49 | status_code=202, data_to_return=response) 50 | # Verifying the endpoint hit 51 | self.assertUrlEqual( 52 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/10dlc/Brand/BRPXS6E/usecases/', 53 | self.client.current_request.url) 54 | 55 | # Verifying the method used 56 | self.assertEqual('GET', self.client.current_request.method) 57 | 58 | @with_response(200) 59 | def test_delete(self): 60 | response = self.client.brand.delete(brand_id='BRPXS6E') 61 | self.client.set_expected_response( 62 | status_code=202, data_to_return=response) 63 | # Verifying the endpoint hit 64 | self.assertUrlEqual( 65 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/10dlc/Brand/BRPXS6E/', 66 | self.client.current_request.url) 67 | 68 | # Verifying the method used 69 | self.assertEqual('DELETE', self.client.current_request.method) 70 | -------------------------------------------------------------------------------- /tests/resources/test_client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from unittest import TestCase 3 | 4 | 5 | class ClientTest(TestCase): 6 | def test_subaccount(self): 7 | from plivo.utils import is_valid_subaccount 8 | self.assertTrue(is_valid_subaccount('SA' + 'X' * 18)) 9 | -------------------------------------------------------------------------------- /tests/resources/test_endpoints.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import PlivoResourceTestCase 3 | from tests.decorators import with_response 4 | 5 | 6 | class EndpointTest(PlivoResourceTestCase): 7 | @with_response(200) 8 | def test_create(self): 9 | self.client.endpoints.create( 10 | username='test', password='test', alias='test') 11 | self.assertEqual(self.client.current_request.method, 'POST') 12 | self.assertUrlEqual( 13 | self.get_voice_url('Endpoint'), self.client.current_request.url) 14 | 15 | @with_response(200) 16 | def test_list(self): 17 | endpoints = self.client.endpoints.list() 18 | # Test if ListResponseObject's __iter__ is working correctly 19 | self.assertEqual(len(list(endpoints)), 2) 20 | self.assertEqual(self.client.current_request.method, 'GET') 21 | self.assertUrlEqual( 22 | self.get_voice_url('Endpoint', limit=20, offset=0), self.client.current_request.url) 23 | 24 | @with_response(200) 25 | def test_get(self): 26 | uuid = '4d04c52e-cea3-4458-bbdb-0bfc314ee7cd' 27 | endpoint = self.client.endpoints.get(uuid) 28 | self.assertResponseMatches(endpoint) 29 | self.assertEqual(self.client.current_request.method, 'GET') 30 | self.assertUrlEqual( 31 | self.get_voice_url('Endpoint', uuid) + 'endpoint_id=' + uuid, self.client.current_request.url) 32 | 33 | @with_response(202) 34 | def test_update(self): 35 | uuid = '4d04c52e-cea3-4458-bbdb-0bfc314ee7cd' 36 | self.client.endpoints.update(uuid, alias='test') 37 | self.assertEqual(self.client.current_request.method, 'POST') 38 | self.assertUrlEqual( 39 | self.get_voice_url('Endpoint', uuid), self.client.current_request.url) 40 | 41 | @with_response(204) 42 | def test_delete(self): 43 | uuid = '4d04c52e-cea3-4458-bbdb-0bfc314ee7cd' 44 | self.client.endpoints.delete(uuid) 45 | self.assertEqual(self.client.current_request.method, 'DELETE') 46 | self.assertUrlEqual( 47 | self.get_voice_url('Endpoint', uuid), self.client.current_request.url) 48 | -------------------------------------------------------------------------------- /tests/resources/test_jwt.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import plivo 4 | import time 5 | from tests.base import PlivoResourceTestCase 6 | from plivo.utils import jwt 7 | 8 | 9 | class AccessTokenTest(PlivoResourceTestCase): 10 | def test_jwt_constructor(self): 11 | self.assertRaisesRegexp(TypeError, "", jwt.AccessToken, 12 | 'ADADADADADADADADADA', 13 | 'qwerty') 14 | 15 | def test_jwt_constructor_auth_id(self): 16 | self.assertRaisesRegexp(plivo.exceptions.ValidationError, 17 | "auth_id should match format .*", 18 | jwt.AccessToken, 19 | 'ADADADADADADADADADA', 'qwerty', 'username') 20 | 21 | def test_jwt_constructor_auth_id(self): 22 | self.assertRaisesRegexp(plivo.exceptions.ValidationError, 23 | "auth_id should match format .*", 24 | jwt.AccessToken, 25 | 'ADADADADADADADADADA', 'qwerty', 'username') 26 | 27 | def test_jwt_constructor_lifetime(self): 28 | self.assertRaisesRegexp(plivo.exceptions.ValidationError, 29 | ".* lifetime .*", 30 | jwt.AccessToken, 31 | 'MADADADADADADADADADA', 32 | 'qwerty', 33 | 'username', 34 | valid_from=int(time.time()), 35 | lifetime=123) 36 | 37 | def test_jwt_constructor_validity(self): 38 | self.assertRaisesRegexp(plivo.exceptions.ValidationError, 39 | "use either lifetime or valid_till", 40 | jwt.AccessToken, 41 | 'MADADADADADADADADADA', 42 | 'qwerty', 43 | 'username', 44 | valid_from=int(time.time()), 45 | valid_till=int(time.time()) - 100, 46 | lifetime=1200) 47 | self.assertRaisesRegexp(plivo.exceptions.ValidationError, 48 | "validity expires .* seconds before it starts", 49 | jwt.AccessToken, 50 | 'MADADADADADADADADADA', 51 | 'qwerty', 52 | 'username', 53 | valid_from=int(time.time()), 54 | valid_till=int(time.time()) - 100) 55 | 56 | def test_jwt(self): 57 | token = jwt.AccessToken('MADADADADADADADADADA', 'qwerty', 'username', valid_from=12121212, lifetime=300, uid='username-12345') 58 | token.add_voice_grants(True, True) 59 | self.assertEqual(True, token.grants['voice']['incoming_allow']) 60 | self.assertNotEqual(False, token.grants['voice']['outgoing_allow']) 61 | -------------------------------------------------------------------------------- /tests/resources/test_lookup.py: -------------------------------------------------------------------------------- 1 | from plivo import exceptions 2 | from tests.base import PlivoResourceTestCase 3 | from tests.decorators import with_response 4 | 5 | 6 | class LookupTest(PlivoResourceTestCase): 7 | @with_response(200) 8 | def test_get(self): 9 | number = '+14154305555' 10 | resp = self.client.lookup.get(number) 11 | self.assertResponseMatches(resp) 12 | self.assertEqual(self.client.current_request.method, 'GET') 13 | -------------------------------------------------------------------------------- /tests/resources/test_medias.py: -------------------------------------------------------------------------------- 1 | from plivo import exceptions 2 | from tests.base import PlivoResourceTestCase 3 | from tests.decorators import with_response 4 | 5 | 6 | class MediaTest(PlivoResourceTestCase): 7 | @with_response(200) 8 | def test_get_media(self): 9 | media_id = 'media_id' 10 | media = self.client.media.get(media_id) 11 | self.assertResponseMatches(media) 12 | self.assertUrlEqual(self.client.current_request.url, 13 | self.get_url('Media', media_id)) 14 | self.assertEqual(self.client.current_request.method, 'GET') 15 | 16 | @with_response(200) 17 | def test_list(self): 18 | media = self.client.media.list() 19 | self.assertEqual(len(list(media)), 2) 20 | self.assertEqual(self.client.current_request.method, 'GET') 21 | -------------------------------------------------------------------------------- /tests/resources/test_members.py: -------------------------------------------------------------------------------- 1 | from plivo.exceptions import ResourceNotFoundError, ValidationError,\ 2 | PlivoServerError 3 | from tests.base import PlivoPhlosResourceTestCase 4 | from tests.decorators import with_response 5 | 6 | 7 | class PhlosMemberTest(PlivoPhlosResourceTestCase): 8 | 9 | @with_response(404) 10 | def test_phlo_get_invalid_phlo_id_failing(self): 11 | """ 12 | Should fail for invalid phlo id 13 | """ 14 | with self.assertRaises(ValidationError): 15 | self.client.phlo.get(None) 16 | 17 | with self.assertRaises(PlivoServerError): 18 | self.client.phlo.get(12345) 19 | 20 | with self.assertRaises(PlivoServerError): 21 | self.client.phlo.get('40b2fa01-39a1-43c8-9f6c-xxxxxxxxxxxx') 22 | 23 | with self.assertRaises(PlivoServerError): 24 | phlo_get = self.client.phlo.get('40b2fa01-39a1-43c8-9f6c-7e6a219afe43') 25 | phlo_get.node('conference_bridge', '5c698012954a4399a455eadd') 26 | 27 | with self.assertRaises(PlivoServerError): 28 | phlo_get = self.client.phlo.get('40b2fa01-39a1-43c8-9f6c-7e6a219afe43') 29 | phlo_get.multi_party_call('5c698012954a4399a455eadd') 30 | 31 | with self.assertRaises(PlivoServerError): 32 | phlo_get = self.client.phlo.get('40b2fa01-39a1-43c8-9f6c-7e6a219afe43') 33 | phlo_get.run() 34 | 35 | @with_response(404) 36 | def test_member_actions(self): 37 | """ 38 | Should fail for invalid member actions 39 | """ 40 | with self.assertRaises(ResourceNotFoundError): 41 | self.client.phlo.member( 42 | '40b2fa01-39a1-43c8-9f6c-7e6a219afe43', 43 | '5c698012954a4399a455eadd', 44 | '656432-xxxxx-xxxxx-xxxxx', 45 | 'hold' 46 | ) 47 | 48 | with self.assertRaises(ResourceNotFoundError): 49 | self.client.phlo.member( 50 | 12345, 51 | '5c698012954a4399a455eadd', 52 | '656432-xxxxx-xxxxx-xxxxx', 53 | 'hold' 54 | ) 55 | 56 | @with_response(400) 57 | def test_member_actions_validation(self): 58 | with self.assertRaises(ValidationError): 59 | self.client.phlo.member( 60 | '40b2fa01-39a1-43c8-9f6c-7e6a219afe43', 61 | '5c698012954a4399a455eadd', 62 | '656432-xxxxx-xxxxx-xxxxx', 63 | 'hold' 64 | ) 65 | 66 | with self.assertRaises(ValidationError): 67 | self.client.phlo.member( 68 | '40b2fa01-39a1-43c8-9f6c-7e6a219afe43', 69 | '5c698012954a4399a455eadd', 70 | '656432-xxxxx-xxxxx-xxxxx', 71 | 'mute' 72 | ) 73 | -------------------------------------------------------------------------------- /tests/resources/test_pricings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import plivo 4 | from tests.base import PlivoResourceTestCase 5 | from tests.decorators import with_response 6 | 7 | 8 | class PricingTest(PlivoResourceTestCase): 9 | def test_pricing_get_invalid_params(self): 10 | with self.assertRaises(plivo.exceptions.ValidationError): 11 | self.client.pricing.get('USA') 12 | 13 | @with_response(200) 14 | def test_get(self): 15 | us_pricing = self.client.pricing.get('US') 16 | self.assertResponseMatches(us_pricing, ) 17 | 18 | # Verifying the endpoint hit 19 | self.assertEqual( 20 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Pricing/?country_iso=US', 21 | self.client.current_request.url) 22 | 23 | # Verifying the method used 24 | self.assertEqual('GET', self.client.current_request.method) 25 | 26 | # Verifying the object type returned 27 | self.assertEqual(plivo.resources.pricings.Pricing, 28 | us_pricing.__class__) 29 | 30 | # Verifying if the Account specific changes and parsing happened 31 | self.assertEqual('US', us_pricing.id) 32 | -------------------------------------------------------------------------------- /tests/resources/test_signature.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import plivo 4 | from tests.base import PlivoResourceTestCase 5 | 6 | 7 | class SignatureTest(PlivoResourceTestCase): 8 | def test_signature(self): 9 | self.assertEqual(True, 10 | plivo.utils.validate_signature( 11 | 'https://answer.url', '12345', 12 | 'ehV3IKhLysWBxC1sy8INm0qGoQYdYsHwuoKjsX7FsXc=', 13 | 'my_auth_token')) 14 | 15 | self.assertEqual(False, 16 | plivo.utils.validate_signature( 17 | 'https://answer.url', '12345', 18 | 'ehV3IKhLysWBxC1sy8INm0qGoQYdYsHwuoKjsX7FsXc=', 19 | 'my_auth_tokens')) 20 | -------------------------------------------------------------------------------- /tests/resources/test_token.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tests import PlivoResourceTestCase 3 | from tests.decorators import with_response 4 | 5 | 6 | class TokenTest(PlivoResourceTestCase): 7 | @with_response(200) 8 | def test_create(self): 9 | self.client.token.create("MAXXXXXXXXXXXXXXXXXX") 10 | self.assertEqual(self.client.current_request.method, 'POST') 11 | self.assertUrlEqual( 12 | self.get_voice_url('JWT/Token'), self.client.current_request.url) 13 | -------------------------------------------------------------------------------- /tests/resources/test_transcriptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from datetime import datetime 4 | 5 | import plivo 6 | from tests.base import PlivoResourceTestCase 7 | from tests.decorators import with_response 8 | 9 | 10 | class TranscriptionTest(PlivoResourceTestCase): 11 | @with_response(200) 12 | def test_get(self): 13 | 14 | transcription = self.client.transcriptions.get_transcription('e12d05fe-6979-485c-83dc-9276114dba3b') 15 | 16 | self.assertResponseMatches(transcription) 17 | 18 | # Verifying the endpoint hit 19 | self.assertEqual( 20 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Transcription/e12d05fe-6979-485c-83dc-9276114dba3b/', 21 | self.client.current_request.url) 22 | 23 | # Verifying the method used 24 | self.assertEqual('GET', self.client.current_request.method) 25 | 26 | self.assertEqual('\nSpeaker 0: Scan just to be safe. If you notice any error messages, please let me know immediately. They can help us diagnose the issue better. If it continues to freeze, we might need to look into your system performance. I can guide you through checking your task manager if that helps.\n\nSometimes, background processes can use up a lot of resources. I under', transcription.transcription) 27 | 28 | @with_response(201) 29 | def test_create(self): 30 | self.client.transcriptions.create_transcription( 31 | recording_id='8605287e-1e1a-4341-8235-23574357d6f1') 32 | 33 | # self.assertResponseMatches(transcription) 34 | 35 | # Verifying the endpoint hit 36 | # self.assertUrlEqual( 37 | # self.get_voice_url('Transcription', '8605287e-1e1a-4341-8235-23574357d6f1'), self.client.current_request.url) 38 | self.assertEqual( 39 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Transcription/8605287e-1e1a-4341-8235-23574357d6f1/', 40 | self.client.current_request.url) 41 | 42 | # Verifying the method used 43 | self.assertEqual('POST', self.client.current_request.method) 44 | 45 | # self.assertEqual('transcription in progress',transcription.message) 46 | 47 | @with_response(202) 48 | def test_delete(self): 49 | transcription = self.client.transcriptions.delete_transcription( 50 | '8605287e-1e1a-4341-8235-23574357d6f1') 51 | 52 | self.assertResponseMatches(transcription) 53 | 54 | # Verifying the endpoint hit 55 | self.assertEqual( 56 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Transcription/8605287e-1e1a-4341-8235-23574357d6f1/', 57 | self.client.current_request.url) 58 | 59 | # Verifying the method used 60 | self.assertEqual('DELETE', self.client.current_request.method) 61 | 62 | self.assertEqual('request accepted', transcription.message) -------------------------------------------------------------------------------- /tests/resources/test_verify.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from plivo import exceptions 3 | from tests.base import PlivoResourceTestCase 4 | from tests.decorators import with_response 5 | 6 | 7 | class SessionTest(PlivoResourceTestCase): 8 | def test_create_session(self): 9 | expected_response = {'session_uuid': 'adsdafkjadshf123123'} 10 | self.client.set_expected_response( 11 | status_code=202, data_to_return=expected_response) 12 | 13 | test_session = self.client.verify_session.create( 14 | recipient='1234567890') 15 | 16 | self.assertEqual( 17 | self.client.current_request.url, 18 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Verify/Session/') 19 | self.assertEqual(self.client.current_request.method, 'POST') 20 | self.assertEqual(test_session.session_uuid, 21 | expected_response['session_uuid']) 22 | 23 | def test_create_session_without_recipient(self): 24 | self.assertRaises( 25 | exceptions.ValidationError, 26 | self.client.verify_session.create 27 | ) 28 | 29 | @with_response(200) 30 | def test_get(self): 31 | session_uuid = 'session_uuid' 32 | session = self.client.verify_session.get(session_uuid) 33 | self.assertResponseMatches(session) 34 | self.assertUrlEqual(self.client.current_request.url, 35 | self.get_url('Verify', 'Session', session_uuid)) 36 | self.assertEqual(self.client.current_request.method, 'GET') 37 | 38 | @with_response(200) 39 | def test_list(self): 40 | messages = self.client.verify_session.list() 41 | # Test if ListResponseObject's __iter__ is working correctly 42 | self.assertEqual(len(list(messages)), 20) 43 | self.assertUrlEqual(self.client.current_request.url, 44 | self.get_url('Verify', 'Session')) 45 | self.assertEqual(self.client.current_request.method, 'GET') 46 | 47 | @with_response(200) 48 | def test_validate(self): 49 | session_uuid = '1234567' 50 | expected_response = {'message': 'session validated successfully.'} 51 | self.client.set_expected_response( 52 | status_code=200, data_to_return=expected_response) 53 | 54 | test_session = self.client.verify_session.validate( 55 | session_uuid=session_uuid, otp='123456') 56 | 57 | self.assertEqual( 58 | self.client.current_request.url, 59 | 'https://api.plivo.com/v1/Account/MAXXXXXXXXXXXXXXXXXX/Verify/Session/1234567/') 60 | self.assertEqual(self.client.current_request.method, 'POST') 61 | self.assertEqual(test_session.message, 62 | expected_response['message']) 63 | 64 | -------------------------------------------------------------------------------- /tests/resources/test_verifycallerids.py: -------------------------------------------------------------------------------- 1 | from tests.base import PlivoResourceTestCase 2 | from tests.decorators import with_response 3 | 4 | 5 | class VerifyCalleridTest(PlivoResourceTestCase): 6 | @with_response(201) 7 | def test_initiate(self): 8 | self.client.verify_callerids.initiate_verify(phone_number='917708772011', 9 | alias='test', 10 | channel='call') 11 | self.assertEqual(self.client.current_request.method, 'POST') 12 | self.assertUrlEqual( 13 | self.get_voice_url('VerifiedCallerId'), self.client.current_request.url) 14 | 15 | @with_response(201) 16 | def test_verify(self): 17 | verification_uuid = 'eeab1477-e59b-4821-9e61-fd5847c2a5db' 18 | self.client.verify_callerids.verify_caller_id( 19 | verification_uuid='eeab1477-e59b-4821-9e61-fd5847c2a5db', otp='610534') 20 | self.assertEqual(self.client.current_request.method, 'POST') 21 | self.assertUrlEqual( 22 | self.get_voice_url('VerifiedCallerId', 'Verification', verification_uuid), self.client.current_request.url) 23 | 24 | @with_response(200) 25 | def test_delete(self): 26 | phone_number = "917708772011" 27 | self.client.verify_callerids.delete_verified_caller_id( 28 | phone_number) 29 | self.assertEqual(self.client.current_request.method, 'DELETE') 30 | self.assertUrlEqual( 31 | self.get_voice_url('VerifiedCallerId', phone_number), self.client.current_request.url) 32 | 33 | @with_response(200) 34 | def test_get(self): 35 | phone_number = "917708772011" 36 | self.client.verify_callerids.get_verified_caller_id(phone_number) 37 | self.assertEqual(self.client.current_request.method, 'GET') 38 | self.assertUrlEqual( 39 | self.get_voice_url('VerifiedCallerId', phone_number), self.client.current_request.url) 40 | 41 | @with_response(200) 42 | def test_update(self): 43 | phone_number = "917708772011" 44 | self.client.verify_callerids.update_verified_caller_id( 45 | phone_number=phone_number, 46 | alias='test123') 47 | self.assertEqual(self.client.current_request.method, 'POST') 48 | self.assertUrlEqual( 49 | self.get_voice_url('VerifiedCallerId', phone_number), self.client.current_request.url) 50 | 51 | @with_response(200) 52 | def test_list(self): 53 | self.client.verify_callerids.list_verified_caller_id() 54 | self.assertEqual(self.client.current_request.method, 'GET') 55 | self.assertUrlEqual( 56 | self.get_voice_url('VerifiedCallerId'), self.client.current_request.url) 57 | -------------------------------------------------------------------------------- /tests/xml/__init__.py: -------------------------------------------------------------------------------- 1 | from .test_responseElement import ResponseElementTest 2 | from .test_recordElement import RecordElementTest 3 | from .test_getDigitsElement import GetDigitsElementTest 4 | from .test_getInputElement import GetInputElementTest 5 | from .test_messageElement import MessageElementTest 6 | -------------------------------------------------------------------------------- /tests/xml/test_breakElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class BreakElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | time = "1000ms" 10 | strength = "x-strong" 11 | expected_response = 'Break of one second or same as paragraph ' 12 | element = plivoxml.ResponseElement() 13 | response = element.add( 14 | plivoxml.SpeakElement("Break of", "Polly.Joey").add( 15 | plivoxml.BreakElement().set_strength(strength).set_time(time) 16 | ).add_cont("one second or same as paragraph") 17 | ).to_string(False) 18 | self.assertXmlEqual(response, expected_response) 19 | -------------------------------------------------------------------------------- /tests/xml/test_emphasisElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class EmphasisElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = '' \ 10 | 'This is Test' \ 11 | 'This is TestThis is Test' \ 13 | 'This is Test' \ 14 | 'This is TestThis is Test' \ 15 | 'This is Test' 16 | level = "strong" 17 | content_break = 'This is Test' 18 | strength_break = 'strong' 19 | time_break = '250ms' 20 | 21 | content_lang = 'This is Test' 22 | xmllang_lang = "it" 23 | 24 | content_emphasis = 'This is Test' 25 | level_emphasis = 'strong' 26 | 27 | content_phoneme = 'This is Test' 28 | alphabet_phoneme = "ipa" 29 | ph_phoneme = "təmei̥ɾou̥" 30 | 31 | content_prosody = "This is Test" 32 | volume_prosody = "+6dB" 33 | rate_prosody = "x-high" 34 | pitch_prosody = "low" 35 | 36 | content_say_as = 'This is Test' 37 | interpret_as_say_as = "spell-out" 38 | format_say_as = "" 39 | 40 | content_sub = "This is Test" 41 | alias_sub = "World Wide Web Consortium" 42 | 43 | content_w = "This is Test" 44 | role_w = "claws:VV0" 45 | 46 | element = plivoxml.ResponseElement() 47 | response = element.add( 48 | plivoxml.SpeakElement("").add( 49 | plivoxml.EmphasisElement().set_level(level).add_break( 50 | strength=strength_break 51 | ).add_lang( 52 | content_lang, 53 | xmllang=xmllang_lang 54 | ).add_emphasis( 55 | content_emphasis, 56 | level=level_emphasis 57 | ).add_phoneme( 58 | content_phoneme, 59 | alphabet=alphabet_phoneme, 60 | ph=ph_phoneme 61 | ).add_prosody( 62 | content_prosody, 63 | pitch=pitch_prosody 64 | ).add_say_as( 65 | content_say_as, 66 | interpret_as=interpret_as_say_as, 67 | format=format_say_as 68 | ).add_sub( 69 | content_sub, 70 | alias=alias_sub, 71 | ).add_w( 72 | content_w, 73 | role_w 74 | ) 75 | ) 76 | ).to_string(False) 77 | self.assertXmlEqual(response, expected_response) 78 | -------------------------------------------------------------------------------- /tests/xml/test_getDigitsElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from plivo import plivoxml 3 | from tests import PlivoXmlTestCase 4 | 5 | 6 | class GetDigitsElementTest(TestCase, PlivoXmlTestCase): 7 | def test_set_methods(self): 8 | expected_response = 'This is test' \ 12 | 'This is test' 13 | 14 | action = 'https://foo.example.com' 15 | method = 'GET' 16 | timeout = 100 17 | digitTimeout = 10 18 | finishOnKey = '#' 19 | numDigits = 2 20 | retries = True 21 | redirect = False 22 | playBeep = False 23 | validDigits = '*' 24 | invalidDigitsSound = 'http://foo.audio.url' 25 | log = True 26 | 27 | content_speak = 'This is test' 28 | voice_speak = 'WOMAN' 29 | language_speak = 'en-US' 30 | loop_speak = 2 31 | loop_play = 2 32 | content_play = 'This is test' 33 | 34 | element = plivoxml.ResponseElement() 35 | response = element.add( 36 | plivoxml.GetDigitsElement().set_action(action).set_method(method). 37 | set_redirect(redirect).set_timeout(timeout).set_play_beep(playBeep) 38 | .set_finish_on_key(finishOnKey).set_digit_timeout(digitTimeout). 39 | set_timeout(timeout).set_num_digits(numDigits).set_retries(retries) 40 | .set_valid_digits(validDigits).set_invalid_digits_sound( 41 | invalidDigitsSound).set_log(log).add_speak( 42 | content=content_speak, 43 | voice=voice_speak, 44 | language=language_speak, 45 | loop=loop_speak).add_play( 46 | content=content_play, loop=loop_play)).to_string(False) 47 | self.assertXmlEqual(response, expected_response) 48 | -------------------------------------------------------------------------------- /tests/xml/test_getInputElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from plivo import plivoxml 3 | from tests import PlivoXmlTestCase 4 | 5 | 6 | class GetInputElementTest(TestCase, PlivoXmlTestCase): 7 | def test_set_methods(self): 8 | expected_response = 'This is testThis is test' \ 15 | '' 16 | 17 | action = 'https://foo.example.com' 18 | method = 'GET' 19 | inputType = 'speech' 20 | executionTimeout = 100 21 | digitEndTimeout = 50 22 | speechEndTimeout = 25 23 | finishOnKey = '#' 24 | numDigits = 2 25 | speechModel = 'default' 26 | hints = '1 2 3' 27 | language = 'en-US' 28 | interimSpeechResultsCallback = 'https://bar.example.com' 29 | interimSpeechResultsCallbackMethod = 'POST' 30 | redirect = False 31 | log = True 32 | 33 | content_speak = 'This is test' 34 | voice_speak = 'WOMAN' 35 | language_speak = 'en-US' 36 | loop_speak = 2 37 | loop_play = 2 38 | content_play = 'This is test' 39 | 40 | element = plivoxml.ResponseElement() 41 | response = element.add( 42 | plivoxml.GetInputElement().set_action(action).set_method(method). 43 | set_input_type(inputType).set_execution_timeout(executionTimeout). 44 | set_digit_end_timeout(digitEndTimeout).set_speech_end_timeout(speechEndTimeout). 45 | set_finish_on_key(finishOnKey).set_num_digits(numDigits). 46 | set_hints(hints).set_interim_speech_results_callback(interimSpeechResultsCallback). 47 | set_interim_speech_results_callback_method(interimSpeechResultsCallbackMethod).set_log(log). 48 | set_redirect(redirect).set_language(language).set_speech_model(speechModel). 49 | add_speak( 50 | content=content_speak, 51 | voice=voice_speak, 52 | language=language_speak, 53 | loop=loop_speak).add_play( 54 | content=content_play, loop=loop_play) 55 | ).to_string(False) 56 | self.assertXmlEqual(response, expected_response) 57 | -------------------------------------------------------------------------------- /tests/xml/test_hangupElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class HangupElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | schedule = 60 10 | reason = "rejected" 11 | expected_response = '' 12 | 13 | element = plivoxml.ResponseElement() 14 | response = element.add( 15 | plivoxml.HangupElement().set_reason(reason).set_schedule(schedule) 16 | ).to_string(False) 17 | self.assertXmlEqual(response, expected_response) 18 | -------------------------------------------------------------------------------- /tests/xml/test_messageElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class MessageElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | 10 | expected_response = 'this is test' \ 12 | '' 13 | src = '1202322222' 14 | dst = '1203443444<1203443445' 15 | type = 'sms' 16 | callbackUrl = 'http://foo.example.com' 17 | callbackMethod = 'GET' 18 | content = 'this is test' 19 | 20 | element = plivoxml.ResponseElement() 21 | response = element.add( 22 | plivoxml.MessageElement(content).set_src(src).set_dst(dst) 23 | .set_type(type).set_callback_url(callbackUrl).set_callback_method( 24 | callbackMethod)).to_string(False) 25 | self.assertXmlEqual(response, expected_response) 26 | -------------------------------------------------------------------------------- /tests/xml/test_numberElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class NumberElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = 'This is Test' 11 | 12 | content = 'This is Test' 13 | send_digits = 'wwww2410' 14 | send_on_preanswer = True 15 | send_digits_mode = "" 16 | 17 | element = plivoxml.ResponseElement() 18 | response = element.add( 19 | plivoxml.DialElement().add( 20 | plivoxml.NumberElement(content).set_send_digits(send_digits).set_send_on_preanswer( 21 | send_on_preanswer 22 | ).set_send_digits_mode( 23 | send_digits_mode 24 | ) 25 | ) 26 | ).to_string(False) 27 | self.assertXmlEqual(response, expected_response) 28 | -------------------------------------------------------------------------------- /tests/xml/test_phonemeElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class ElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | alphabet = "ipa" 10 | ph = "təmei̥ɾou̥" 11 | expected_response = 'Well' 13 | 14 | element = plivoxml.ResponseElement() 15 | response = element.add( 16 | plivoxml.SpeakElement("").add( 17 | plivoxml.PhonemeElement("Well").set_alphabet(alphabet).set_ph(ph) 18 | ) 19 | ).to_string(False) 20 | self.assertXmlEqual(response, expected_response) 21 | -------------------------------------------------------------------------------- /tests/xml/test_playElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class PlayElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = 'This is test' 10 | 11 | loop = 1 12 | content = 'This is test' 13 | element = plivoxml.ResponseElement() 14 | 15 | response = element.add( 16 | plivoxml.PlayElement(content).set_loop(loop) 17 | ).to_string(False) 18 | self.assertXmlEqual(response, expected_response) 19 | -------------------------------------------------------------------------------- /tests/xml/test_recordElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from plivo import plivoxml 3 | from tests import PlivoXmlTestCase 4 | 5 | 6 | class RecordElementTest(TestCase, PlivoXmlTestCase): 7 | def test_set_methods(self): 8 | expected_response = '' \ 13 | '' 14 | action = 'https://foo.example.com' 15 | method = 'GET' 16 | fileFormat = 'wav' 17 | redirect = False 18 | timeout = 100 19 | maxLength = 10 20 | recordSession = False 21 | startOnDialAnswer = False 22 | playBeep = False 23 | finishOnKey = '#' 24 | transcriptionType = 'hybrid' 25 | transcriptionUrl = 'https://foo.example.com' 26 | transcriptionMethod = 'GET' 27 | callbackUrl = 'https://foo.example.com' 28 | callbackMethod = 'GET' 29 | 30 | element = plivoxml.ResponseElement() 31 | response = element.add( 32 | plivoxml.RecordElement().set_action(action).set_method(method) 33 | .set_file_format(fileFormat).set_redirect(redirect).set_timeout( 34 | timeout).set_max_length(maxLength).set_play_beep(playBeep) 35 | .set_finish_on_key(finishOnKey).set_record_session(recordSession). 36 | set_start_on_dial_answer(startOnDialAnswer).set_transcription_type( 37 | transcriptionType).set_transcription_url(transcriptionUrl) 38 | .set_transcription_method(transcriptionMethod).set_callback_url( 39 | callbackUrl).set_callback_method(callbackMethod)).to_string(False) 40 | self.assertXmlEqual(response, expected_response) 41 | -------------------------------------------------------------------------------- /tests/xml/test_redirectElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class RedirectElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = 'This is Test' 10 | 11 | method = "POST" 12 | content = 'This is Test' 13 | 14 | element = plivoxml.ResponseElement() 15 | response = element.add( 16 | plivoxml.RedirectElement(content).set_method(method) 17 | ).to_string(False) 18 | 19 | self.assertXmlEqual(response, expected_response) 20 | -------------------------------------------------------------------------------- /tests/xml/test_sElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class SElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = '' \ 10 | 'This is TestThis is ' \ 11 | 'TestThis is TestThis is Test' \ 14 | 'This is TestThis is Test' \ 15 | 'This is Test' 16 | 17 | content_break = 'This is Test' 18 | strength_break = 'strong' 19 | time_break = '250ms' 20 | 21 | content_lang = 'This is Test' 22 | xmllang_lang = "it" 23 | 24 | content_emphasis = 'This is Test' 25 | level_emphasis = 'strong' 26 | 27 | content_phoneme = 'This is Test' 28 | alphabet_phoneme = "ipa" 29 | ph_phoneme = "təmei̥ɾou̥" 30 | 31 | content_prosody = "This is Test" 32 | volume_prosody = "+6dB" 33 | rate_prosody = "x-high" 34 | pitch_prosody = "low" 35 | 36 | content_say_as = 'This is Test' 37 | interpret_as_say_as = "spell-out" 38 | # TODO: need to ask the value 39 | format_say_as = "" 40 | 41 | content_sub = "This is Test" 42 | alias_sub = "World Wide Web Consortium" 43 | 44 | content_w = "This is Test" 45 | role_w = "claws:VV0" 46 | 47 | element = plivoxml.ResponseElement() 48 | response = element.add( 49 | plivoxml.SpeakElement("").add( 50 | plivoxml.SElement().add_break( 51 | strength=strength_break 52 | ).add_emphasis( 53 | content_emphasis, 54 | level=level_emphasis 55 | ).add_lang( 56 | content_lang, 57 | xmllang=xmllang_lang 58 | ).add_phoneme( 59 | content_phoneme, 60 | alphabet=alphabet_phoneme, 61 | ph=ph_phoneme 62 | ).add_prosody( 63 | content_prosody, 64 | volume=volume_prosody, 65 | rate=rate_prosody, 66 | pitch=pitch_prosody 67 | ).add_say_as( 68 | content_say_as, 69 | interpret_as=interpret_as_say_as, 70 | format=format_say_as 71 | ).add_sub( 72 | content_sub, 73 | alias=alias_sub, 74 | ).add_w( 75 | content_w, 76 | role_w 77 | ) 78 | ) 79 | ).to_string(False) 80 | self.assertXmlEqual(response, expected_response) 81 | -------------------------------------------------------------------------------- /tests/xml/test_sayAsElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class SayAsElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = '' \ 10 | 'This is Test' 11 | interpret_as = "spell-out" 12 | format = "application/ssml+xml" 13 | content = 'This is Test' 14 | 15 | element = plivoxml.ResponseElement() 16 | 17 | response = element.add( 18 | plivoxml.SpeakElement("").add( 19 | plivoxml.SayAsElement(content).set_interpret_as(interpret_as).set_format(format) 20 | ) 21 | ).to_string(False) 22 | 23 | self.assertXmlEqual(response, expected_response) 24 | -------------------------------------------------------------------------------- /tests/xml/test_streamElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from plivo.exceptions import ValidationError 5 | from tests import PlivoXmlTestCase 6 | 7 | 8 | class StreamElementTest(TestCase, PlivoXmlTestCase): 9 | def test_set_methods(self): 10 | expected_response = """ 11 | wss://test.url 12 | """ 13 | 14 | content = 'wss://test.url' 15 | bidirectional = True 16 | extraHeaders = "a=1,b=2" 17 | keepCallAlive = True 18 | 19 | element = plivoxml.ResponseElement() 20 | response = element.add( 21 | plivoxml.StreamElement(content, bidirectional=bidirectional, extraHeaders=extraHeaders, keepCallAlive=keepCallAlive) 22 | ).to_string(False) 23 | self.assertXmlEqual(response, expected_response) 24 | 25 | def test_validations_on_elements(self): 26 | expected_error = 'bidirectional must be a boolean value.' 27 | 28 | actual_error = '' 29 | content = 'wss://test.url' 30 | bidirectional = "hello" 31 | try: 32 | plivoxml.StreamElement(content=content, bidirectional=bidirectional) 33 | except ValidationError as e: 34 | print("Error: ", str(e)) 35 | actual_error = str(e) 36 | self.assertXmlEqual(expected_error, actual_error) 37 | -------------------------------------------------------------------------------- /tests/xml/test_subElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class SubElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = 'substitution example W3C' 10 | alias="World Wide Web Consortium" 11 | 12 | element = plivoxml.ResponseElement() 13 | response = element.add( 14 | plivoxml.SpeakElement("substitution example ").add( 15 | plivoxml.SubElement("W3C").set_alias(alias) 16 | ) 17 | ).to_string(False) 18 | self.assertXmlEqual(response, expected_response) 19 | -------------------------------------------------------------------------------- /tests/xml/test_userElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class UserElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = 'This is Test' 11 | 12 | content = 'This is Test' 13 | send_digits = 'wwww2410' 14 | send_on_preanswer = True 15 | sip_headers = 'head1=val1,head2=val2' 16 | 17 | element = plivoxml.ResponseElement() 18 | 19 | response = element.add( 20 | plivoxml.DialElement().add( 21 | plivoxml.UserElement(content).set_send_digits(send_digits).set_send_on_preanswer( 22 | send_on_preanswer 23 | ).set_sip_headers(sip_headers) 24 | ) 25 | ).to_string(False) 26 | self.assertXmlEqual(response, expected_response) 27 | -------------------------------------------------------------------------------- /tests/xml/test_wElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class WElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = 'This is Test' \ 10 | 'This is Test' \ 11 | 'This is TestThis is Test' \ 14 | 'This is TestThis is Test' \ 15 | '' 16 | 17 | role = 'claws:VV0' 18 | content = 'This is Test' 19 | 20 | content_break = 'This is Test' 21 | strength_break = 'strong' 22 | time_break = '250ms' 23 | 24 | content_emphasis = 'This is Test' 25 | level_emphasis = 'strong' 26 | 27 | content_phoneme = 'This is Test' 28 | alphabet_phoneme = "ipa" 29 | ph_phoneme = "təmei̥ɾou̥" 30 | 31 | content_prosody = "This is Test" 32 | volume_prosody = "+6dB" 33 | rate_prosody = "x-high" 34 | pitch_prosody = "low" 35 | 36 | 37 | content_say_as = 'This is Test' 38 | interpret_as_say_as = "spell-out" 39 | format_say_as = "" 40 | 41 | content_sub = "This is Test" 42 | alias_sub = "World Wide Web Consortium" 43 | 44 | element = plivoxml.ResponseElement() 45 | response = element.add( 46 | plivoxml.SpeakElement("").add( 47 | plivoxml.WElement(content).set_role( 48 | role 49 | ).add_break( 50 | strength=strength_break 51 | ).add_emphasis( 52 | content_emphasis, 53 | level=level_emphasis 54 | ).add_phoneme( 55 | content_phoneme, 56 | alphabet=alphabet_phoneme, 57 | ph=ph_phoneme 58 | ).add_prosody( 59 | content_prosody, 60 | volume=volume_prosody, 61 | rate=rate_prosody, 62 | pitch=pitch_prosody 63 | ).add_say_as( 64 | content_say_as, 65 | interpret_as=interpret_as_say_as, 66 | format=format_say_as 67 | ).add_sub( 68 | content_sub, 69 | alias=alias_sub, 70 | )) 71 | ).to_string(False) 72 | self.assertXmlEqual(response, expected_response) 73 | -------------------------------------------------------------------------------- /tests/xml/test_waitElement.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from plivo import plivoxml 4 | from tests import PlivoXmlTestCase 5 | 6 | 7 | class WaitElementTest(TestCase, PlivoXmlTestCase): 8 | def test_set_methods(self): 9 | expected_response = '' 10 | 11 | length = 1 12 | silence = True 13 | min_silence = 1 14 | beep = True 15 | 16 | element = plivoxml.ResponseElement() 17 | response = element.add( 18 | plivoxml.WaitElement().set_length(length).set_silence( 19 | silence 20 | ).set_min_silence(min_silence).set_beep(beep) 21 | ).to_string(False) 22 | 23 | self.assertXmlEqual(response, expected_response) 24 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (https://tox.readthedocs.io/) is a tool for running tests in multiple 2 | # virtualenvs. This configuration file helps to run the test suite on all 3 | # supported Python versions. To use it, "pip install tox" and then run "tox" 4 | # from this directory. 5 | 6 | [tox] 7 | envlist = 8 | py27 9 | py34 10 | py35 11 | py36 12 | py37 13 | py38 14 | py39 15 | py310 16 | py311 17 | pypy 18 | 19 | [testenv] 20 | commands = pytest --cov=plivo --cov-report={posargs} 21 | 22 | # tox doesn't install deps automatically after env is created 23 | # so some deps are repeated here and in setup.py 24 | # see https://github.com/tox-dev/tox/issues/496#issuecomment-289854245 25 | 26 | deps = 27 | coverage 28 | pytest 29 | pytest-cov 30 | httmock 31 | six 32 | lxml 33 | 34 | setenv = 35 | PYTHONPATH = {toxinidir}/ 36 | --------------------------------------------------------------------------------