Compare commits

...

5 Commits

Author SHA1 Message Date
Daniel Tesfai 1369e0b885 removing tests from pipeline for now, fixed more lint errors 2020-03-16 23:18:51 -04:00
Daniel Tesfai 770993fef2 Merge branch 'master' of github.com:dtesfai/voipms-python 2020-03-16 22:58:27 -04:00
Daniel Tesfai ac8e6a7b18 fixing lint errors 2020-03-16 22:58:09 -04:00
Daniel Tesfai 0d6857c016
Create pythonpackage.yml 2020-03-16 09:33:48 -04:00
Daniel Tesfai f5e6a7a15f removed templates folder as it was used for development purposes 2019-09-09 02:10:45 -04:00
33 changed files with 176 additions and 154 deletions

35
.github/workflows/pythonpackage.yml vendored Normal file
View File

@ -0,0 +1,35 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: Python package
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.5, 3.6, 3.7, 3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- name: Lint with flake8
run: |
pip install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

5
.gitignore vendored
View File

@ -24,10 +24,10 @@ lib
lib64
share
pyvenv.cfg
templates
*.egg-info
man
include
credentials.py
# Unit test / coverage reports
.coverage
@ -44,3 +44,6 @@ htmlcov*
# Visual Studio Code
.vscode/
# Development
templates/

View File

@ -1,5 +1,3 @@
import os
import sys
from setuptools import setup, find_packages
with open("README.md", "r") as fh:
@ -35,4 +33,4 @@ setup(
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries :: Python Modules",
],
)
)

View File

@ -1,3 +0,0 @@
@property
def {{method_name}}(self):
return self.{{subdir}}.{{method_name}}

View File

@ -1,7 +0,0 @@
class {{class_name}}():
def __init__(self, base):
self.method = "{{endpoint}}"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)

View File

@ -1 +0,0 @@
from voipms.api.{{category_name}}.{{method_name}} import {{class_name}}

View File

@ -1 +0,0 @@
self._{{method_name}} = None

View File

@ -1,66 +0,0 @@
from jinja2 import Environment, FileSystemLoader
file_loader = FileSystemLoader('.')
env = Environment(loader=file_loader)
print("Which category does this endpoint fall under?")
subdir_index = int(input("1: accounts, 2: call_detail_records, 3: dids, 4: general, 5: voicemail: "))
subdir = ("accounts", "call_detail_records", "dids", "general", "voicemail")[subdir_index - 1]
endpoint = input("endpoint name? ")
method = input("method name? ")
filename = "../voipms/api/{}/{}.py".format(subdir, method)
template = env.get_template('endpoint.j2')
method_split = method.split('_')
class_name = ''.join((i.title() for i in method_split))
output = template.render(endpoint=endpoint, class_name=class_name)
with open(filename, 'a') as file:
file.writelines(output)
filename = "../voipms/api/{}/__init__.py".format(subdir)
template = env.get_template('import.j2')
method_split = method.split('_')
class_name = ''.join((i.title() for i in method_split))
output_import = template.render(method_name=method, class_name=class_name, category_name=subdir)
template = env.get_template('initialize.j2')
output_init = template.render(method_name=method)
template = env.get_template('subdir_init.j2')
output_property = template.render(method_name=method, class_name=class_name)
with open(filename, 'r') as file:
contents = file.readlines()
pos = 0
for k, v in enumerate(contents):
if v == "\n":
contents.insert(k, output_import + "\n")
pos = k
break
for k, v in enumerate(contents[pos+2:], pos+2):
if v == "\n":
contents.insert(k, output_init + "\n")
break
contents.append("\n\n" + output_property)
with open(filename, 'w') as file:
file.writelines(contents)
filename = "../voipms/api/__init__.py"
template = env.get_template('dir_init.j2')
output = template.render(subdir=subdir, method_name=method)
with open(filename, 'a') as file:
file.writelines("\n\n" + output)

View File

@ -1,5 +0,0 @@
@property
def {{method_name}}(self):
if self._{{method_name}} is None:
self._{{method_name}} = {{class_name}}(self.base)
return self._{{method_name}}

0
tests/__init__.py Normal file
View File

31
tests/tests.py Normal file
View File

@ -0,0 +1,31 @@
from __future__ import absolute_import
from __future__ import print_function
from unittest.mock import patch
from voipms.api import Client
from unittest.mock import MagicMock
import credentials
def test_instantiation():
username = credentials.username
password = credentials.password
client = Client(username, password)
return client
@patch('voipms.api.general.balance')
def test_make_get_request(mock_requests):
mock_response_obj = MagicMock()
mock_response_obj.json = {"test": "test"}
mock_response_obj.status_code = 200
mock_requests.get.return_value = mock_response_obj
username = credentials.username
password = credentials.password
client = Client(username, password)
res = client.registration_status.fetch()
assert res == {"test": "test"}

View File

@ -1,23 +1,21 @@
import os
import platform
import json
import requests
from voipms.base.exceptions import VoipException
class Client(object):
def __init__(self, username=None, password=None):
self.username = username or os.environ.get('VOIPMS_ACCOUNT_USER')
self.password = password or os.environ.get('VOIPMS_API_TOKEN')
self.api_base = "https://voip.ms/api/v1/rest.php"
if not self.username or not self.password:
raise VoipException("Credentials are required to create a Client")
self.auth = (self.username, self.password)
# Domains
self._accounts = None
self._call_detail_records = None
self._dids = None
@ -26,7 +24,7 @@ class Client(object):
def request(self, method, auth=None, params={}):
auth = auth or self.auth
params["api_username"] = auth[0]
params["api_password"] = auth[1]
params["method"] = method
@ -34,7 +32,7 @@ class Client(object):
response = requests.get(self.api_base, params=params)
data = json.loads(response.text)
if data['status'] and data['status'] != 'success':
err_code = data['status']
raise VoipException(err_code)
@ -79,7 +77,7 @@ class Client(object):
@property
def balance(self):
return self.general.balance
@property
def ip(self):
return self.general.ip
@ -130,4 +128,4 @@ class Client(object):
@property
def messages(self):
return self.voicemail.messages
return self.voicemail.messages

View File

@ -1,6 +1,7 @@
from voipms.api.accounts.subaccount import Subaccount
from voipms.api.accounts.registration_status import RegistrationStatus
class Accounts():
def __init__(self, base):
self._subaccount = None
@ -18,4 +19,4 @@ class Accounts():
def registration_status(self):
if self._registration_status is None:
self._registration_status = RegistrationStatus(self.base)
return self._registration_status
return self._registration_status

View File

@ -2,6 +2,6 @@ class RegistrationStatus():
def __init__(self, base):
self.method = "getRegistrationStatus"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,19 +2,19 @@ class Subaccount():
def __init__(self, base):
self.method = ""
self.base = base
def create(self, params={}):
self.method = "createSubAccount"
return self.base.request(self.method, params=params)
def delete(self, params={}):
self.method = "delSubAccount"
return self.base.request(self.method, params=params)
def fetch(self, params={}):
self.method = "getSubAccounts"
return self.base.request(self.method, params=params)
def set(self, params={}):
self.method = "setSubAccount"
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -3,9 +3,9 @@ from voipms.api.call_detail_records.records import Records
from voipms.api.call_detail_records.rates import Rates
from voipms.api.call_detail_records.termination_rates import TerminationRates
class CallDetailRecords():
def __init__(self, base):
self.base = base
self._billing = None
self._records = None
@ -34,4 +34,4 @@ class CallDetailRecords():
def termination_rates(self):
if self._termination_rates is None:
self._termination_rates = TerminationRates(self.base)
return self._termination_rates
return self._termination_rates

View File

@ -2,6 +2,6 @@ class Billing():
def __init__(self, base):
self.method = "getCallBilling"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,6 +2,6 @@ class Rates():
def __init__(self, base):
self.method = "getRates"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,6 +2,6 @@ class Records():
def __init__(self, base):
self.method = "getCDR"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,6 +2,6 @@ class TerminationRates():
def __init__(self, base):
self.method = "getTerminationRates"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -1,6 +1,7 @@
from voipms.api.dids.search import Search
from voipms.api.dids.sms import SMS
class DIDs():
def __init__(self, base):
self._search = None
@ -30,4 +31,4 @@ class DIDs():
def fetch(self, params={}):
self.method = "getDIDsInfo"
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,11 +2,11 @@ class Search():
def __init__(self, base):
self.method = ""
self.base = base
def canada(self, params={}):
self.method = "searchDIDsCAN"
return self.base.request(self.method, params=params)
def usa(self, params={}):
self.method = "searchDIDsUSA"
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,7 +2,7 @@ class SMS():
def __init__(self, base):
self.method = ""
self.base = base
def fetch(self, params={}):
self.method = "getSMS"
return self.base.request(self.method, params=params)
@ -13,4 +13,4 @@ class SMS():
def set(self, params={}):
self.method = "setSMS"
return self.base.request(self.method, params={})
return self.base.request(self.method, params={})

View File

@ -4,6 +4,7 @@ from voipms.api.general.transaction_history import TransactionHistory
from voipms.api.general.countries import Countries
from voipms.api.general.languages import Languages
class General():
def __init__(self, base):
self._balance = None
@ -42,4 +43,4 @@ class General():
def languages(self):
if self._languages is None:
self._languages = Languages(self.base)
return self._languages
return self._languages

View File

@ -2,6 +2,6 @@ class Balance():
def __init__(self, base):
self.method = "getBalance"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,6 +2,6 @@ class Countries():
def __init__(self, base):
self.method = "getCountries"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,6 +2,6 @@ class IP():
def __init__(self, base):
self.method = "getIP"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,6 +2,6 @@ class Languages():
def __init__(self, base):
self.method = "getLanguages"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,6 +2,6 @@ class TransactionHistory():
def __init__(self, base):
self.method = "getTransactionHistory"
self.base = base
def fetch(self, params={}):
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -1,5 +1,6 @@
from voipms.api.voicemail.messages import Messages
class Voicemail():
def __init__(self, base):
self._messages = None
@ -25,4 +26,4 @@ class Voicemail():
def set(self, params={}):
self.method = "setVoicemail"
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -2,11 +2,11 @@ class Messages():
def __init__(self, base):
self.method = ""
self.base = base
def fetch(self, params={}):
self.method = "getVoicemailMessages"
return self.base.request(self.method, params=params)
def delete(self, params={}):
self.method = "delMessages"
return self.base.request(self.method, params=params)
return self.base.request(self.method, params=params)

View File

@ -1,6 +1,5 @@
from __future__ import absolute_import, division, print_function
import sys
class VoipException(Exception):
def __init__(self, err_code=""):
@ -11,20 +10,23 @@ class VoipException(Exception):
'account_with_dids': 'The Account has DIDs assigned to it.',
'api_not_enabled': 'API has not been enabled or has been disabled',
'api_limit_exceeded': 'API requests limit per minute has been reached',
'cancel_failed': "The cancellation wasn't completed.", 'can_have_only_one_profile_without_pin': 'The conference can just have one profile member without pin',
'cancel_failed': "The cancellation wasn't completed.",
'can_have_only_one_profile_without_pin': 'The conference can just have one profile member without pin',
'conference_member_relation_not_found': 'There is no relation between the profile member and the conference.',
'did_in_use': 'DID Number is already in use',
'duplicated_pin': 'The given pin has been duplicated',
'error_deleting_msg': 'Error when deleting message',
'error_moving_msg': 'Error when move the voicemail message to folder',
'existing_did': "You can't set a callback to an existing VoIP.ms DID number", 'exceeds_file_size': 'The file exceeds the limite size allowed.',
'existing_did': "You can't set a callback to an existing VoIP.ms DID number",
'exceeds_file_size': 'The file exceeds the limite size allowed.',
'forwards_exceeded': 'Your account is limited to 4 forward entries',
'invalid_account': 'This is not a valid account',
'invalid_address': 'Address is missing or the format is invalid.',
'invalid_admin': 'This is not a valid admin',
'invalid_agent_ring_timeout': 'This is not a valid Agent ring time out value',
'invalid_allowedcodecs': 'One of the codecs provided is invalidFormat and Values: ulaw;g729;gsm;all',
'invalid_attachid': "The given ID is invalid or doesn't exist.", 'invalid_announce_join_leave': 'This is not a valid "Announce join leave"',
'invalid_attachid': "The given ID is invalid or doesn't exist.",
'invalid_announce_join_leave': 'This is not a valid "Announce join leave"',
'invalid_announce_only_user': 'This is not a valid "Announce only user"',
'invalid_announce_position_frequency': 'This is not a valid Announce position frequency',
'invalid_announce_round_seconds': 'This is not a valid "Announce round seconds"',
@ -54,7 +56,10 @@ class VoipException(Exception):
'invalid_conference': 'This is not a valid Conference ID',
'invalid_countryid': 'This is not a valid Country ID',
'invalid_city': 'City is missing or the format is invalid.',
'invalid_country': 'Country is missing or the format is invalid, must be in format ISO 3166-1 alpha-2, example: US, CA, etc. (You can use the values returned by the method getCountries)',
'invalid_country': (
'Country is missing or the format is invalid, must be in format ISO 3166-1 alpha-2, '
'example: US, CA, etc. (You can use the values returned by the method getCountries)'
),
'invalid_credentials': 'Username or Password is incorrect',
'invalid_date': 'This is not a valid dateFormat is: yyyy-mm-dd',
'invalid_datetime': 'This is not a valid datetimeFormat is: yyyy-mm-dd hh:mm:ss',
@ -105,7 +110,9 @@ class VoipException(Exception):
'invalid_ip_iax2': 'Do not provide an IP address for IAX2',
'invalid_ivr': 'This is not a valid IVR',
'invalid_jitter_buffer': 'This is not a valid "jitter buffer" value',
'invalid_join_empty_type': "This is not a valid 'JoinWhenEmpty' Type for a Queue", 'invalid_join_announcement': "This is not a valid 'Join Announcement' Type for a Queue", 'invalid_language': 'This is not a valid LanguageShould be: es/en/fr',
'invalid_join_empty_type': "This is not a valid 'JoinWhenEmpty' Type for a Queue",
'invalid_join_announcement': "This is not a valid 'Join Announcement' Type for a Queue",
'invalid_language': 'This is not a valid LanguageShould be: es/en/fr',
'invalid_lastname': 'Lastname is missing or the format is invalid.',
'invalid_listened': 'This is not a valid Listened value',
'invalid_location': 'This is not a valid Location',
@ -130,15 +137,24 @@ class VoipException(Exception):
'invalid_number_us': 'You have entered a USA number (not valid in this portability process).',
'invalid_number_fax': 'The Fax number can not be ported into our network',
'invalid_number_exist': 'The number is already in our network',
'invalid_numbermembers': 'The element format of multiple data is not correct or it size does not match with other elements',
'invalid_numbermembers': (
'The element format of multiple data is not correct '
'or it size does not match with other elements'
),
'invalid_order': 'This is not a valid "order" value',
'invalid_package': 'This is not a valid Package',
'invalid_password': 'This is not a valid passwordVoicemail: Must be 4 DigitsSubAccounts: More than 6 chars, Must Contain Alphanumeric and !#$%&/()=?*[]_:.,{}+-',
'invalid_password': (
'This is not a valid password, Voicemail: Must be 4 Digits, '
'SubAccounts: More than 6 chars, Must Contain Alphanumeric and !#$%&/()=?*[]_:.,{}+-'
),
'invalid_password_auth': 'Do not provide a Password for IP Authentication',
'invalid_password_lessthan_8characters_long': 'This is not a valid password (Less than 8 characters long)',
'invalid_password_missing_uppercase': 'This is not a valid password (Missing upper case character)',
'invalid_password_missing_lowercase': 'This is not a valid password (Missing lower case character)',
'invalid_password_ilegal_characters': 'This is not a valid password (Allowed characters: Alphanumeric and ! # $ % & / ( ) = ? * [ ] _ : . , { } + -)',
'invalid_password_ilegal_characters': (
'This is not a valid password (Allowed characters: '
'Alphanumeric and ! # $ % & / ( ) = ? * [ ] _ : . , { } + -)'
),
'invalid_password_missing_number': 'This is not a valid password (Missing a number)',
'invalid_pause': 'This is not a valid Pause',
'invalid_payment': 'This is not a valid Payment',
@ -152,7 +168,8 @@ class VoipException(Exception):
'invalid_province': 'This is not a valid Province',
'invalid_provider_name': 'You must provide the service provider name',
'invalid_provider_account': 'You must provide your account # with the current provider',
'invalid_portingid': "The given ID is invalid or doesn't exist.", 'invalid_porttype': 'Must provide a valid port type.',
'invalid_portingid': "The given ID is invalid or doesn't exist.",
'invalid_porttype': 'Must provide a valid port type.',
'invalid_port_status': 'The status code is invalid. (You can use the values returned by the method getListStatus)',
'invalid_quantity': 'This is not a valid quantity',
'invalid_query': 'This is not a valid Query',
@ -181,7 +198,10 @@ class VoipException(Exception):
'invalid_recording_sound_participants_unmuted': '"participants unmuted" is not a valid recording',
'invalid_report_hold_time_agent': 'This is not a valid Report hold time agent',
'invalid_resellerclient': 'This is not a valid Reseller Client',
'invalid_resellernextbilling': 'This is not a valid Reseller Next Billing date, date should not be set in the past.',
'invalid_resellernextbilling': (
'This is not a valid Reseller Next Billing date, '
'date should not be set in the past.'
),
'invalid_resellerpackage': 'This is not a valid Reseller Package',
'invalid_response_timeout': 'This is not a valid ResponseTimeOut',
'invalid_retry_timer': 'This is not a valid Retry timer',
@ -215,7 +235,10 @@ class VoipException(Exception):
'invalid_timecondition': 'This is not a valid Time Condition',
'invalid_timeout': 'This is not a valid timeout',
'invalid_timerange': 'This is not a valid Timer Range',
'invalid_timezone': 'This is not a valid TimezoneCDR and resellerCDR: Must be numericVoicemail: Values from getTimezone',
'invalid_timezone': (
'This is not a valid TimezoneCDR and resellerCDR: '
'Must be numericVoicemail: Values from getTimezone'
),
'invalid_type': 'This is not a valid Type',
'invalid_to_number': 'This is not a valid destination number',
'invalid_username': 'This is not a valid Username',
@ -228,7 +251,7 @@ class VoipException(Exception):
'invalid_urgent': 'This is not valid urgent value',
'invalid_zip': 'Zip Code is missing or the format is invalid.',
'ip_not_enabled': 'This IP is not enabled for API use',
'limit_reached': 'You have reached the maximum number of messages allowed per day.- SMS limit using the API.- Fax limit applies using any method.',
'limit_reached': 'You have reached the maximum number of messages allowed per day.',
'max_phonebook': 'Your account is limited to 8 SIP, IAX or SIP URI members',
'member_already_included': 'The member has been included already',
'members_exceeded': 'You have reached the maximum allowed entries for the Phonebook',
@ -284,13 +307,19 @@ class VoipException(Exception):
'missing_folder': 'folder was not provided',
'missing_forwarding': 'Forwarding was not provided',
'missing_id': 'ID was not provided',
'missing_if_announce_position_enabled_report_estimated_hold_time': "'If announce position enabled report estimated hold time' type was not provided", 'missing_internationalroute': 'International Route was not provided',
'missing_if_announce_position_enabled_report_estimated_hold_time': (
"'If announce position enabled report "
"estimated hold time' type was not provided"
),
'missing_internationalroute': 'International Route was not provided',
'missing_ip': 'You need to provide an IP if you select IP Authentication Method',
'missing_ip_h323': 'You must enter an IP Address for H.323',
'missing_ivr': 'IVR was not provided',
'missing_join_when_empty': "'JoinWhenEmpty' type was not provided", 'missing_language': 'Language was not provided',
'missing_join_when_empty': "'JoinWhenEmpty' type was not provided",
'missing_language': 'Language was not provided',
'missing_lastname': 'Lastname was not provided',
'missing_leave_when_empty': "'LeaveWhenEmpty' type was not provided", 'missing_listened': 'Listened code was not provided',
'missing_leave_when_empty': "'LeaveWhenEmpty' type was not provided",
'missing_listened': 'Listened code was not provided',
'missing_location': 'Location was not provided',
'missing_lockinternational': 'Lock International was not provided',
'missing_mailbox': 'Mailbox was not provided',
@ -320,7 +349,9 @@ class VoipException(Exception):
'missing_query': 'Query was not provided',
'missing_recording': 'Recording was not provided',
'missing_report_hold_time_agent': 'Report hold time agent was not provided',
'missing_resellerclient': "Provide a Reseller Client or don't provide a Reseller Package", 'missing_resellerpackage': "Provide a Reseller Package or don't provide a Reseller Client", 'missing_response_timeout': 'ResponseTimeOut was not provided',
'missing_resellerclient': "Provide a Reseller Client or don't provide a Reseller Package",
'missing_resellerpackage': "Provide a Reseller Package or don't provide a Reseller Client",
'missing_response_timeout': 'ResponseTimeOut was not provided',
'missing_ringgroup': 'Ring group was not provided',
'missing_ring_inuse': 'Ring in use was not provided',
'missing_ring_strategy': 'Ring strategy was not provided',
@ -358,7 +389,10 @@ class VoipException(Exception):
'no_base64file': 'File not encoded in base64',
'no_callback': 'There are not Callbacks',
'no_callhunting': 'There are no Call Huntings',
'no_callstatus': 'No Call Status was provided.One of the following parameters needs to be set to "1": answered, noanswer, busy, failed',
'no_callstatus': (
'No Call Status was provided. One of the following parameters needs to be set to "1": '
'answered, noanswer, busy, failed'
),
'no_cdr': 'There are no CDR entries for the filter',
'no_change_billingtype': 'Imposible change DID billing plan',
'no_client': 'There are no Clients',
@ -383,7 +417,8 @@ class VoipException(Exception):
'no_sms': 'There are no SMS messages',
'no_timecondition': 'There are no Time Conditions',
'note_toolong': 'The note exceeds character size limit',
'order_failed': "The order wasn't completed.", 'provider_outofservice': 'One of our providers is out of service',
'order_failed': "The order wasn't completed.",
'provider_outofservice': 'One of our providers is out of service',
'recording_in_use_did': 'You have a DID using this Recording',
'recording_in_use_queue': 'You have a Calling Queue using this Recording',
'recording_in_use_ivr': 'You have an IVR using this Recording',
@ -392,7 +427,9 @@ class VoipException(Exception):
'repeated_ip': 'You already have a Subaccount using this IP and Protocol',
'reserved_ip': 'This is a reserved IP used by VoIP.ms or other Companies',
'same_did_billingtype': 'The Billing Type provided and DID billing type are the same',
'sipuri_in_phonebook': "This SIPURI can't be deleted, it is mapped in the phonebook", 'sent_fail': "The Fax Message it wasn't send.", 'sms_toolong': 'The SMS message exceeds 160 characters',
'sipuri_in_phonebook': "This SIPURI can't be deleted, it is mapped in the phonebook",
'sent_fail': "The Fax Message it wasn't send.",
'sms_toolong': 'The SMS message exceeds 160 characters',
'sms_failed': 'The SMS message was not sent',
'tls_error': 'Theres was a TLS error, please try later.',
'Unable_to_purchase': 'Unable to purchase DIDs',
@ -411,8 +448,7 @@ class VoipException(Exception):
err_desc = err_code_map[self.err_code] or self.err_code
return "API Call failed as: {}".format(err_desc)
class VoipRestException(VoipException):
def __str__(self):
return self.err_code or "<empty message>"
return self.err_code or "<empty message>"

View File

@ -8,4 +8,4 @@ def of(d):
:param dict d: A dict to strip.
:return dict: A dict with unset values removed.
"""
return {k: v for k, v in d.items() if v != unset}
return {k: v for k, v in d.items() if v != unset}