pax_global_header00006660000000000000000000000064143514143460014517gustar00rootroot0000000000000052 comment=35ccbd8a4acea11bcb41e8152292b68a0f99159b home-assistant-ecosystem-python-hole-c989781/000077500000000000000000000000001435141434600212315ustar00rootroot00000000000000home-assistant-ecosystem-python-hole-c989781/.github/000077500000000000000000000000001435141434600225715ustar00rootroot00000000000000home-assistant-ecosystem-python-hole-c989781/.github/workflows/000077500000000000000000000000001435141434600246265ustar00rootroot00000000000000home-assistant-ecosystem-python-hole-c989781/.github/workflows/python.yml000066400000000000000000000011621435141434600266720ustar00rootroot00000000000000name: Python package and lint on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: [ 3.8, 3.9, "3.10" ] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip python setup.py install - name: Black Code Formatter uses: jpetrucciani/black-check@22.12.0 home-assistant-ecosystem-python-hole-c989781/CHANGES.rst000066400000000000000000000012231435141434600230310ustar00rootroot00000000000000Changes ======= 0.8.0 - 20221223 ---------------- - Add auth to summary API call (thanks @mib1185) 20211123 - 0.7.0 ---------------- - Allow later ``async_timeout`` - Remove loop (thanks @bdraco) 20211023 - 0.6.0 ---------------- 20211023 - 0.6.0 ---------------- - Add version (thanks @Olen) - Update Python releases 20200325 - 0.5.1 ---------------- - Reduce verbosity (thanks @fermulator) 20190817 - 0.5.0 ---------------- - Add '=' for api_token parameter (thanks @johnluetke) - Add versions method (thanks @wfriesen) 20190115 - 0.4.0 ----------------- - Add enable/disable functions 201806014 - 0.3.0 ----------------- - Initial release home-assistant-ecosystem-python-hole-c989781/LICENSE000066400000000000000000000021261435141434600222370ustar00rootroot00000000000000MIT License Copyright (c) 2018-2022 Fabian Affolter Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. home-assistant-ecosystem-python-hole-c989781/MANIFEST.in000066400000000000000000000000201435141434600227570ustar00rootroot00000000000000include LICENSE home-assistant-ecosystem-python-hole-c989781/README.rst000066400000000000000000000034001435141434600227150ustar00rootroot00000000000000python-hole =========== Python API for interacting with a xyz-hole instance. You know the thing that is blocking Ads by manipulating your DNS requests and run on your single board computer or on other hardware with different operating systems. This module is consuming the details provided by the endpoint ``api.php`` only for now. If you wonder why the weird name and that the usage of xzy-hole instead of the real name, please contact the trademark holder. They were unhappy with original name of the module and came up with very funny ideas which were not feasible or match the existing naming standards. Also, it should remind us that a community is a vital part of every Open Source project. This module is not supported or endorsed by that particular trademark holder. The development happens independently, they are not providing resources and the module may no longer work if they breaking their API endpoint. Installation ------------ The module is available from the `Python Package Index `_. .. code:: bash $ pip3 install hole On a Fedora-based system. .. code:: bash $ sudo dnf -y install python-hole For Nix or NixOS is a `pre-packed module `_ available. The lastest release is usually present in the ``unstable`` channel. .. code:: bash $ nix-env -iA nixos.python3Packages.hole Usage ----- The file ``example.py`` contains an example about how to use this module. Roadmap ------- There are more features on the roadmap but there is no ETA because I prefer to support Open Source projects were third party contributions are appreciated. License ------- ``python-hole`` is licensed under MIT, for more details check LICENSE. home-assistant-ecosystem-python-hole-c989781/example.py000066400000000000000000000033241435141434600232400ustar00rootroot00000000000000"""Example for the usage of the hole module.""" import asyncio import json import aiohttp from hole import Hole API_TOKEN = "YOUR_API_TOKEN" async def main(): """Get the data from a *hole instance.""" async with aiohttp.ClientSession() as session: data = Hole("192.168.0.215", session) await data.get_versions() print(json.dumps(data.versions, indent=4, sort_keys=True)) print( "Version:", data.core_current, "Latest:", data.core_latest, "Update available:", data.core_update, ) print( "FTL:", data.ftl_current, "Latest:", data.ftl_latest, "Update available:", data.ftl_update, ) print( "Web:", data.web_current, "Latest:", data.web_latest, "Update available:", data.web_update, ) await data.get_data() # Get the raw data print(json.dumps(data.data, indent=4, sort_keys=True)) print("Status:", data.status) print("Domains being blocked:", data.domains_being_blocked) async def disable(): """Get the data from a *hole instance.""" async with aiohttp.ClientSession() as session: data = Hole("192.168.0.215", session, api_token=API_TOKEN) await data.disable() async def enable(): """Get the data from a *hole instance.""" async with aiohttp.ClientSession() as session: data = Hole("192.168.0.215", session, api_token=API_TOKEN) await data.enable() if __name__ == "__main__": asyncio.run(main()) asyncio.run(disable()) asyncio.run(enable()) home-assistant-ecosystem-python-hole-c989781/hole/000077500000000000000000000000001435141434600221605ustar00rootroot00000000000000home-assistant-ecosystem-python-hole-c989781/hole/__init__.py000066400000000000000000000153771435141434600243060ustar00rootroot00000000000000"""*hole API Python client.""" import asyncio import logging import socket import aiohttp import async_timeout from . import exceptions _LOGGER = logging.getLogger(__name__) _INSTANCE = "{schema}://{host}/{location}/api.php" class Hole(object): """A class for handling connections with a *hole instance.""" def __init__( self, host, session, location="admin", tls=False, verify_tls=True, api_token=None, ): """Initialize the connection to a *hole instance.""" self._session = session self.tls = tls self.verify_tls = verify_tls self.schema = "https" if self.tls else "http" self.host = host self.location = location self.api_token = api_token self.data = {} self.versions = {} self.base_url = _INSTANCE.format( schema=self.schema, host=self.host, location=self.location ) async def get_data(self): """Get details of a *hole instance.""" params = "summaryRaw&auth={}".format(self.api_token) try: async with async_timeout.timeout(5): response = await self._session.get(self.base_url, params=params) _LOGGER.debug("Response from *hole: %s", response.status) self.data = await response.json() _LOGGER.debug(self.data) except (asyncio.TimeoutError, aiohttp.ClientError, socket.gaierror): msg = "Can not load data from *hole: {}".format(self.host) _LOGGER.error(msg) raise exceptions.HoleConnectionError(msg) async def get_versions(self): """Get version information of a *hole instance.""" params = "versions" try: async with async_timeout.timeout(5): response = await self._session.get(self.base_url, params=params) _LOGGER.debug("Response from *hole: %s", response.status) self.versions = await response.json() _LOGGER.debug(self.versions) except (asyncio.TimeoutError, aiohttp.ClientError, socket.gaierror): msg = "Can not load data from *hole: {}".format(self.host) _LOGGER.error(msg) raise exceptions.HoleConnectionError(msg) async def enable(self): """Enable DNS blocking on a *hole instance.""" if self.api_token is None: _LOGGER.error("You need to supply an api_token to use this") return params = "enable=True&auth={}".format(self.api_token) try: async with async_timeout.timeout(5): response = await self._session.get(self.base_url, params=params) _LOGGER.debug("Response from *hole: %s", response.status) data = await response.json() _LOGGER.debug(data) except (asyncio.TimeoutError, aiohttp.ClientError, socket.gaierror): msg = "Can not load data from *hole: {}".format(self.host) _LOGGER.error(msg) raise exceptions.HoleConnectionError(msg) async def disable(self, duration=True): """Disable DNS blocking on a *hole instance.""" if self.api_token is None: _LOGGER.error("You need to supply an api_token to use this") return params = "disable={}&auth={}".format(duration, self.api_token) try: async with async_timeout.timeout(5): response = await self._session.get(self.base_url, params=params) _LOGGER.debug("Response from *hole: %s", response.status) data = await response.json() _LOGGER.debug(data) except (asyncio.TimeoutError, aiohttp.ClientError, socket.gaierror): msg = "Can not load data from *hole: {}".format(self.host) _LOGGER.error(msg) raise exceptions.HoleConnectionError(msg) @property def status(self): """Return the status of the *hole instance.""" return self.data["status"] @property def unique_clients(self): """Return the unique clients of the *hole instance.""" return self.data["unique_clients"] @property def unique_domains(self): """Return the unique domains of the *hole instance.""" return self.data["unique_domains"] @property def ads_blocked_today(self): """Return the ads blocked today of the *hole instance.""" return self.data["ads_blocked_today"] @property def ads_percentage_today(self): """Return the ads percentage today of the *hole instance.""" return self.data["ads_percentage_today"] @property def clients_ever_seen(self): """Return the clients_ever_seen of the *hole instance.""" return self.data["clients_ever_seen"] @property def dns_queries_today(self): """Return the dns queries today of the *hole instance.""" return self.data["dns_queries_today"] @property def domains_being_blocked(self): """Return the domains being blocked of the *hole instance.""" return self.data["domains_being_blocked"] @property def queries_cached(self): """Return the queries cached of the *hole instance.""" return self.data["queries_cached"] @property def queries_forwarded(self): """Return the queries forwarded of the *hole instance.""" return self.data["queries_forwarded"] @property def ftl_current(self): """Return the current version of FTL of the *hole instance.""" return self.versions["FTL_current"] @property def ftl_latest(self): """Return the latest version of FTL of the *hole instance.""" return self.versions["FTL_latest"] @property def ftl_update(self): """Return wether an update of FTL of the *hole instance is available.""" return self.versions["FTL_update"] @property def core_current(self): """Return the current version of the *hole instance.""" return self.versions["core_current"] @property def core_latest(self): """Return the latest version of the *hole instance.""" return self.versions["core_latest"] @property def core_update(self): """Return wether an update of the *hole instance is available.""" return self.versions["core_update"] @property def web_current(self): """Return the current version of the web interface of the *hole instance.""" return self.versions["web_current"] @property def web_latest(self): """Return the latest version of the web interface of the *hole instance.""" return self.versions["FTL_latest"] @property def web_update(self): """Return wether an update of web interface of the *hole instance is available.""" return self.versions["FTL_update"] home-assistant-ecosystem-python-hole-c989781/hole/exceptions.py000066400000000000000000000003511435141434600247120ustar00rootroot00000000000000"""Exceptions for *hole API Python client""" class HoleError(Exception): """General HoleError exception occurred.""" pass class HoleConnectionError(HoleError): """When a connection error is encountered.""" pass home-assistant-ecosystem-python-hole-c989781/setup.py000066400000000000000000000024621435141434600227470ustar00rootroot00000000000000#!/usr/bin/env python3 """Setup file for the *hole API Python client.""" import os from setuptools import setup here = os.path.abspath(os.path.dirname(__file__)) # Get the long description from the relevant file with open(os.path.join(here, "README.rst"), encoding="utf-8") as desc: long_description = desc.read() setup( name="hole", version="0.8.0", description="Python API for interacting with *hole.", long_description=long_description, url="https://github.com/home-assistant-ecosystem/python-hole", download_url="https://github.com/home-assistant-ecosystem/python-hole/releases", author="Fabian Affolter", author_email="fabian@affolter-engineering.ch", license="MIT", install_requires=[ "aiohttp<4", "async_timeout>4,<5", ], packages=["hole"], python_requires=">=3.9", zip_safe=True, classifiers=[ "Development Status :: 3 - Alpha", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Topic :: Utilities", ], )