From 25634ad4be141336f48008c2ad87392106f729e3 Mon Sep 17 00:00:00 2001 From: Maff Date: Tue, 14 May 2019 00:12:38 +0100 Subject: [PATCH] refactoring and better standardisation --- Py3CX/__init__.py | 99 +++++++++++++++++++++++++++++++++++++++++++++ py3cx.py | 100 ---------------------------------------------- requirements.txt | 1 + 3 files changed, 100 insertions(+), 100 deletions(-) create mode 100644 Py3CX/__init__.py delete mode 100644 py3cx.py create mode 100644 requirements.txt diff --git a/Py3CX/__init__.py b/Py3CX/__init__.py new file mode 100644 index 0000000..bb8d0a8 --- /dev/null +++ b/Py3CX/__init__.py @@ -0,0 +1,99 @@ +# Environment var definitions +ENV_URI='TCX_SITE_URI' +ENV_AUTH_USER='TCX_API_AUTH_USERNAME' +ENV_AUTH_PASS='TCX_API_AUTH_PASSWORD' + +class APIError(Exception): + pass +class ValidationError(Exception): + pass + +class Request: + from requests import ConnectionError, exceptions + def __init__(self, uri, verify): + from requests import Session + self.base="%s/api/{}" % uri + self.sess=Session() + self.sess.verify=verify + + def get(self, api, params=None, expect=200): + try: + resp=self.sess.get(url=self.base.format(api), params=params) + assert (resp.status_code==expect) + except AssertionError: + raise APIError("Assertion error when handling API response; response code was %s, but expected response was %s" % (resp.status_code, expect)) + except exceptions.SSLError as e: + raise APIError("TLS error returned when communicating; use tls_verify=False or check leaf certs: %s" % str(e)) + except exceptions.BaseHTTPError as e: + raise APIError("HTTP error raised when communicating: %s" % str(e)) + except ConnectionError as e: + raise APIError("ConnectionError raised when communicating: %s" % str(e)) + return resp + + def post(self, api, params, expect=200): + try: + resp=self.sess.post(url=self.base.format(api), json=params) + assert (resp.status_code==expect) + except AssertionError: + raise APIError("Assertion error when handling API response; response code was %s, but expected response was %s" % (resp.status_code, expect)) + except exceptions.SSLError as e: + raise APIError("TLS error returned when communicating; use tls_verify=False or check leaf certs: %s" % str(e)) + except exceptions.BaseHTTPError as e: + raise APIError("HTTP error raised when communicating: %s" % str(e)) + except ConnectionError as e: + raise APIError("ConnectionError raised when communicating: %s" % str(e)) + except Exception as e: + raise APIError("Other exception raised during API call: %s" % str(e)) + return resp + +class Extension(object): + def __init__(self, parent, params): + self.tcx=parent + self.params=params + self.load() + + def load(self): + res=self.tcx.rq.get('ExtensionList') + self.timestamp=res.headers.get('date') + res=list(filter(lambda ext: ext['Number'] == self.params, res.json()['list'])) + assert (len(res)>0), "No extension found for: %s" % self.params + assert (len(res)==1), "More than one extension found for %s" % self.params + res=res[0] + self.number=res['Number'] + self.firstname=res['FirstName'] + self.surname=res['LastName'] + self.name="%s %s" % (self.firstname, self.surname) + self.dnd=res['DND'] + self.status=res['CurrentProfile'] + self.mail=res['Email'] + self.cli=res['OutboundCallerId'] + self.mobile=res['MobileNumber'] + self.online=res['IsRegistered'] + + +class Py3CX: + def __init__(self, uri=None, tls_verify=True): + from os import getenv + self.uri=uri if uri is not None and uri.startswith('http') else getenv(ENV_URI, None) + assert (self.uri is not None and self.uri.startswith('http')), "Please provide URI" + self.uname=getenv(ENV_AUTH_USER, None) + self.passw=getenv(ENV_AUTH_PASS, None) + self.rq=Request(uri=self.uri, verify=tls_verify) + + def authenticate(self, username=None, password=None): + if username is not None and password is not None: + self.uname=username + self.passw=password + assert (self.uname is not None and self.passw is not None), "Authentication information needed" + self.rq.post('login', params={ + 'Username': self.uname, + 'Password': self.passw}) + + @property + def authenticated(self): + try: + self.rq.get('CurrentUser') + except APIError: + return False + return True + diff --git a/py3cx.py b/py3cx.py deleted file mode 100644 index 775efc1..0000000 --- a/py3cx.py +++ /dev/null @@ -1,100 +0,0 @@ -class Py3CX: - class _ApiCallBuilder: - apibase='api' - uri='{}/{}/{}' - # TODO: check login URI component - login={ - 'uri':'', - 'method':'POST', - 'expect':200 - } - # Returns current username, displayed initial(s), 3CX phone system version, current roles, email addresses and information about privileges and enabled features - CurrentUser={ - 'uri':'CurrentUser', - 'method':'GET', - 'expepct':200 - } - # Returns a list of current calls, including active trunk where available - ActiveCalls={ - 'uri':'activeCalls', - 'method':'GET', - 'expepct':200 - } - SystemStatus={ - 'uri':'SystemStatus', - 'method':'GET', - 'expepct':200 - } - # Returns event log entries; optional ?count=INT argument to retrieve specific number of events - EventLog={ - 'uri':'{}/getEventLog'.format(SystemStatus['uri']), - 'method':'GET', - 'expect':200 - } - # Returns bytes of recording space used and allocated by quota, also returns recording states. - SystemStatusAdditional={ - 'uri':'{}/AdditionalStatus'.format(SystemStatus['uri']), - 'method':'GET', - 'expect':200 - } - # Returns state of system health (whether firewall, configured trunks and phones meet 3CX support baselines) - SystemStatusHealth={ - 'uri':'{}/GetSingleStatus'.format(SystemStatus['uri']), - 'method':'GET', - 'expect':200 - } - # TODO: API call 'SystemStatus/getDbInformation' - # TODO: API call 'TrunkList' - # TODO: API call 'InboundRulesList' - # TODO: API call 'OutboundRuleList' - # TODO: API call 'ActivityLog' - # TODO: API call 'ActivityLog/getKeepDays' - # TODO: API call 'ActivityLog/getKeepLogs' - # TODO: API call 'ActivityLog/getLogLevel' - # TODO: API call 'BackupAndRestoreList' - # TODO: API call 'CallLog' - # TODO: API call 'capture/getInterfaces' - # TODO: API call 'CustomParametersList' - # TODO: API call 'ExtensionList' - # TODO: API call 'GroupList' - # TODO: API call 'IpBlackList' - # TODO: API call 'IVRList' - # TODO: API call 'License' - # TODO: API call 'NumberBlackList' - # TODO: API call 'NumberBlackList/new' POST (followed by calls to 'edit/update' and 'edit/save') - # TODO: API call 'NumberBlackList/delete' POST {Ids: ["(number)"]} - # TODO: API call 'PhoneList' - # TODO: API call 'QueueList' - # TODO: API call 'RecordingList' - # TODO: API call 'RingGroupList' - # TODO: API call 'SystemPromptList' - # TODO: API call 'SystemPromptList/promptSets' - # TODO: API call 'SystemPromptList/promptSetUpdates' - # TODO: API call 'UpdateChecker/GetFromParams' - # TODO: API call 'updateChecker/check' POST - # TODO: API call 'updateChecker/isDebian8' - # TODO: API call 'crm/serverCrmUpdates' - # TODO: API call 'crm/clientCrmUpdates' - # TODO: API call 'CrmList/CrmSettings' POST - - - def __init__(self, uri=None, account=None): - from os import getenv - if uri==None: - uri=getenv('TCX_URI', None) - if account==None: - account={ - "username": getenv('TCX_USERNAME', None), - "password": getenv('TCX_PASSWORD', None) - } - assert (len(account['username']>1)), "py3cx was initialised without account information, and TCX_USERNAME does not contain a valid username." - assert (len(account['password']>5)), "py3cx was initialised without account information, and TCX_PASSWORD does not contain a valid password." - #Validation for initialisation parameters - assert (len(uri)>0), "uri must be a full URI to your target 3CX server" - assert (uri.startswith('http')), "uri must be a valid HTTP or HTTPS URI" - assert ('username' in account), "account object must contain username property" - assert ('password' in account), "account object must contain password property" - assert (len(account['username'])>1), "account object must contain a valid username" - assert (len(account['password'])>5), "account object must contain a valid password" - self._base=uri - self._account=account \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..663bd1f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +requests \ No newline at end of file