commit f605b65b6c36d2d6b3aa5d987b92f674457dafa6 Author: Daniel Date: Sat Aug 17 22:02:44 2019 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f21dc8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# pyenv +.python-version + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee88c09 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# voipms-python + +A python-based API wrapper for [voip.ms](https://voip.ms/). + +## Installation + +You can install this package from PyPi using [pip](http://www.pip-installer.org/en/latest/), a package manager for Python. Once pip is installed, run + +```sh +pip install --upgrade voipms-python +``` + +You can also install from source with: + +```sh +python setup.py install +``` + +### Requirements + +- Python 2.7+ or 3.4+ + +## Usage + +The library needs to be configured with your account's email and API password, the latter of which can be created [here](https://voip.ms/m/api.php). Set the variables `voipms.email` and `voipms.apikey` to their respective values: + +```python +import voipms + +voipms.email = "test@email.com" +voipms.api_key = "01N0sWTdiutWTHNF" + +# get current account balance +voipms.general.get_balance() + +# remove did from account +voipms.dids.cancel_dids(did="5551234567") +``` diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..5fe4d90 --- /dev/null +++ b/setup.py @@ -0,0 +1,68 @@ +import os +import sys +from setuptools import setup, find_packages +from setuptools.command.test import test as TestCommand + +class PyTest(TestCommand): + user_options = [("pytest-args=", "a", "Arguments to pass to pytest")] + + def initialize_options(self): + TestCommand.initialize_options(self) + self.pytest_args = "-n auto" + + def run_tests(self): + import shlex + import pytest + + errno = pytest.main(shlex.split(self.pytest_args)) + sys.exit(errno) + +here = os.path.abspath(os.path.dirname(__file__)) + +version_contents = {} +with open(os.path.join(here, "voipms", "version.py"), encoding="utf-8") as f: + exec(f.read(), version_contents) + +setup( + name="voipms-python", + version=version_contents["VERSION"], + description="Python wrapper for the voip.ms REST API", + author="Daniel Tesfai", + author_email="danielmtesfai@gmail.com", + url="https://github.com/dtesfai/voipms-python", + license="MIT", + keywords="voipms api", + packages=find_packages(exclude=["tests", "tests.*"]), + zip_safe=False, + install_requires=[ + 'requests >= 2.20; python_version >= "3.0"' + ], + python_requires=">=3.4", + tests_require=[ + "pytest >= 4.6.2, < 4.7", + "pytest-mock >= 1.10.4", + "pytest-xdist >= 1.28.0", + "pytest-cov >= 2.7.1", + # coverage 5.0 pre-releases don't work, and setuptools doesn't ignore + # pre-releases (cf. https://github.com/pypa/setuptools/issues/855) + "coverage >= 4.5.3, < 5", + ], + cmdclass={"test": PyTest}, + project_urls={ + "Bug Tracker": "https://github.com/dtesfai/voipms-python/issues", + "Source Code": "https://github.com/dtesfai/voipms-python", + }, + classifiers=[ + "Development Status :: 2 - Pre-Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Software Development :: Libraries :: Python Modules", + ], +) \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/voipms/__init__.py b/voipms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/voipms/api/__init__.py b/voipms/api/__init__.py new file mode 100644 index 0000000..b0d12e2 --- /dev/null +++ b/voipms/api/__init__.py @@ -0,0 +1,73 @@ +import os +import platform +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 + self._general = None + self._voicemail = None + + 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 + params["content_type"] = "json" + + response = requests.get(self.api_base, params=params).json() + return response + + @property + def accounts(self): + if self._accounts is None: + from voipms.api.accounts import Accounts + self._accounts = Accounts(self) + return self._accounts + + @property + def call_detail_records(self): + if self._call_detail_records is None: + from voipms.api.call_detail_records import CallDetailRecords + self._call_detail_records = CallDetailRecords() + return self._call_detail_records + + @property + def dids(self): + if self._dids is None: + from voipms.api.dids import DIDs + self._dids = DIDs() + return self._dids + + @property + def general(self): + if self._general is None: + from voipms.api.general import General + self._general = General() + return self._general + + @property + def voicemail(self): + if self._voicemail is None: + from voipms.api.voicemail import Voicemail + self._voicemail = Voicemail() + return self._voicemail + + @property + def balance(self): + return self.accounts.balance \ No newline at end of file diff --git a/voipms/api/accounts/__init__.py b/voipms/api/accounts/__init__.py new file mode 100644 index 0000000..91ec980 --- /dev/null +++ b/voipms/api/accounts/__init__.py @@ -0,0 +1,12 @@ +from voipms.api.accounts.balance import Balance + +class Accounts(): + def __init__(self, base): + self._balance = None + self.base = base + + @property + def balance(self): + if self._balance is None: + self._balance = Balance(self.base) + return self._balance \ No newline at end of file diff --git a/voipms/api/accounts/balance.py b/voipms/api/accounts/balance.py new file mode 100644 index 0000000..4738402 --- /dev/null +++ b/voipms/api/accounts/balance.py @@ -0,0 +1,7 @@ +class Balance(): + def __init__(self, base): + self.method = "getBalance" + self.base = base + + def fetch(self, params={}): + return self.base.request(self.method) \ No newline at end of file diff --git a/voipms/api/call_detail_records/__init__.py b/voipms/api/call_detail_records/__init__.py new file mode 100644 index 0000000..3a6007b --- /dev/null +++ b/voipms/api/call_detail_records/__init__.py @@ -0,0 +1,3 @@ +class CallDetailRecords(): + def __init__(self): + pass \ No newline at end of file diff --git a/voipms/api/dids/__init__.py b/voipms/api/dids/__init__.py new file mode 100644 index 0000000..f6534c7 --- /dev/null +++ b/voipms/api/dids/__init__.py @@ -0,0 +1,3 @@ +class DIDs(): + def __init__(self): + pass \ No newline at end of file diff --git a/voipms/api/general/__init__.py b/voipms/api/general/__init__.py new file mode 100644 index 0000000..2873d1a --- /dev/null +++ b/voipms/api/general/__init__.py @@ -0,0 +1,3 @@ +class General(): + def __init__(self): + pass \ No newline at end of file diff --git a/voipms/api/voicemail/__init__.py b/voipms/api/voicemail/__init__.py new file mode 100644 index 0000000..41b8da1 --- /dev/null +++ b/voipms/api/voicemail/__init__.py @@ -0,0 +1,3 @@ +class Voicemail(): + def __init__(self): + self._balance = None \ No newline at end of file diff --git a/voipms/base/__init__.py b/voipms/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/voipms/base/exceptions.py b/voipms/base/exceptions.py new file mode 100644 index 0000000..2a3a542 --- /dev/null +++ b/voipms/base/exceptions.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import, division, print_function + +import sys + +class VoipException(Exception): + pass + +class VoipRestException(VoipException): + def __init__(self, status, message=""): + self.msg = message + self.status = status + + def __str__(self): + return self.msg or "" \ No newline at end of file diff --git a/voipms/base/values.py b/voipms/base/values.py new file mode 100644 index 0000000..b98d915 --- /dev/null +++ b/voipms/base/values.py @@ -0,0 +1,11 @@ +unset = object() + + +def of(d): + """ + Remove unset values from a dict. + + :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} \ No newline at end of file diff --git a/voipms/version.py b/voipms/version.py new file mode 100644 index 0000000..e69de29