From f843db631426cfa7feca7229b7fb535be56c8753 Mon Sep 17 00:00:00 2001 From: Stephen Eisenhauer Date: Thu, 4 May 2023 00:23:46 -0700 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20Apprise=20not?= =?UTF-8?q?ifications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ“ Update README update-docs --- README.md | 2 +- camply/config/__init__.py | 2 + camply/config/file_config.py | 1 + camply/config/notification_config.py | 8 ++ camply/notifications/__init__.py | 2 + camply/notifications/apprise.py | 77 +++++++++++++++++++ .../multi_provider_notifications.py | 2 + docs/command_line_usage.md | 16 +++- docs/examples/example.camply | 3 + docs/how_to_run.md | 2 + docs/index.md | 1 + pyproject.toml | 4 + requirements/requirements-dev.txt | 55 +++++-------- requirements/requirements-prod.txt | 37 ++++++--- 14 files changed, 165 insertions(+), 47 deletions(-) create mode 100755 camply/notifications/apprise.py diff --git a/README.md b/README.md index 97893762..429b8cc5 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ camply campgrounds --search "Fire Lookout Towers" --state CA Search for available campsites, get a notification whenever one becomes available, and continue searching after the first one is found. The below command is using `silent` notifications as an example but camply also supports `Email`, -`Slack`, `Twilio` (SMS), `Pushover`, `Pushbullet`, and `Telegram`. +`Slack`, `Twilio` (SMS), `Pushover`, `Pushbullet`, `Ntfy`, `Apprise`, and `Telegram`. ```commandline camply campsites \ diff --git a/camply/config/__init__.py b/camply/config/__init__.py index 80b10496..529ba254 100644 --- a/camply/config/__init__.py +++ b/camply/config/__init__.py @@ -6,6 +6,7 @@ from .data_columns import CampsiteContainerFields, DataColumns from .file_config import FileConfig from .notification_config import ( + AppriseConfig, EmailConfig, NtfyConfig, PushbulletConfig, @@ -24,6 +25,7 @@ "CampsiteContainerFields", "DataColumns", "FileConfig", + "AppriseConfig", "EmailConfig", "NtfyConfig", "PushbulletConfig", diff --git a/camply/config/file_config.py b/camply/config/file_config.py index 0f47924a..1fed16f4 100644 --- a/camply/config/file_config.py +++ b/camply/config/file_config.py @@ -72,6 +72,7 @@ class FileConfig: "default": "", "notes": "NTFY Notification Topic", }, + APPRISE_URL={"default": "", "notes": "Apprise notification URL"}, RIDB_API_KEY={ "default": "", "notes": "Personal Recreation.gov API Key (not required)", diff --git a/camply/config/notification_config.py b/camply/config/notification_config.py index ee20f734..16f6dccc 100644 --- a/camply/config/notification_config.py +++ b/camply/config/notification_config.py @@ -27,6 +27,14 @@ class PushoverConfig: PUSH_USER: str = getenv("PUSHOVER_PUSH_USER", None) +class AppriseConfig: + """ + Apprise Notification Config Class + """ + + APPRISE_URL: str = getenv("APPRISE_URL", None) + + class EmailConfig: """ Email Notification Config Class diff --git a/camply/notifications/__init__.py b/camply/notifications/__init__.py index e31f35b5..c199c020 100644 --- a/camply/notifications/__init__.py +++ b/camply/notifications/__init__.py @@ -2,6 +2,7 @@ Notifications __init__ file """ +from .apprise import AppriseNotifications from .email_notifications import EmailNotifications from .multi_provider_notifications import CAMPSITE_NOTIFICATIONS, MultiNotifierProvider from .pushbullet import PushbulletNotifications @@ -12,6 +13,7 @@ from .twilio import TwilioNotifications __all__ = [ + "AppriseNotifications", "PushbulletNotifications", "PushoverNotifications", "TelegramNotifications", diff --git a/camply/notifications/apprise.py b/camply/notifications/apprise.py new file mode 100755 index 00000000..21500a38 --- /dev/null +++ b/camply/notifications/apprise.py @@ -0,0 +1,77 @@ +""" +Push Notifications via Apprise +""" + +import logging +from typing import List + +from camply.config import AppriseConfig +from camply.containers import AvailableCampsite +from camply.notifications.base_notifications import BaseNotifications + +logger = logging.getLogger(__name__) +logging.getLogger("apprise").setLevel(logging.ERROR) + + +class AppriseNotifications(BaseNotifications): + """ + Push Notifications via Apprise + """ + + def __init__(self): + super().__init__() + try: + import apprise + except ImportError as ie: + raise RuntimeError( + "Looks like `apprise` isn't installed. Install it with `pip install camply[apprise]`" + ) from ie + + if any( + [ + AppriseConfig.APPRISE_URL is None, + ] + ): + warning_message = ( + "Apprise is not configured properly. To send Apprise notifications " + "make sure to run `camply configure` or set the " + "proper environment variable: `APPRISE_URL`." + ) + logger.error(warning_message) + raise EnvironmentError(warning_message) + self.client = apprise.Apprise() + self.client.add(AppriseConfig.APPRISE_URL) + logger.info("Apprise: will notify specified URL") + + def send_message(self, message: str, **kwargs): + """ + Send a message via Apprise - if environment variables are configured + + Parameters + ---------- + message: str + """ + self.client.notify( + body=message, + title="Camply Notification", + ) + + def send_campsites(self, campsites: List[AvailableCampsite], **kwargs): + """ + Send a message with a campsite object + + Parameters + ---------- + campsites: AvailableCampsite + """ + for campsite in campsites: + message_title, formatted_dict = self.format_standard_campsites( + campsite=campsite, + ) + fields = [f"πŸ•{message_title}", ""] + for key, value in formatted_dict.items(): + fields.append(f"{key}: {value}") + fields.append("") + fields.append("camply, the campsite finder ⛺️") + composed_message = "\n".join(fields) + self.send_message(message=composed_message) diff --git a/camply/notifications/multi_provider_notifications.py b/camply/notifications/multi_provider_notifications.py index f235212b..0ab7fa49 100644 --- a/camply/notifications/multi_provider_notifications.py +++ b/camply/notifications/multi_provider_notifications.py @@ -6,6 +6,7 @@ from typing import Dict, List, Type, Union from camply.containers import AvailableCampsite +from camply.notifications.apprise import AppriseNotifications from camply.notifications.base_notifications import BaseNotifications, NotificationError from camply.notifications.email_notifications import EmailNotifications from camply.notifications.ntfy import NtfyNotifications @@ -22,6 +23,7 @@ "pushover": PushoverNotifications, "email": EmailNotifications, "ntfy": NtfyNotifications, + "apprise": AppriseNotifications, "pushbullet": PushbulletNotifications, "slack": SlackNotifications, "telegram": TelegramNotifications, diff --git a/docs/command_line_usage.md b/docs/command_line_usage.md index 160cba2c..06f38d73 100644 --- a/docs/command_line_usage.md +++ b/docs/command_line_usage.md @@ -145,7 +145,7 @@ and a link to make the booking. Required parameters include `--start-date`, `--e [\*\*_example_](#continue-looking-after-the-first-match-is-found) - `--notifications`: `NOTIFICATIONS` - Enables continuous searching. Types of notifications to receive. Options available - are `pushover`, `email`, `ntfy`, `pushbullet`, `slack`, `telegram`, `twilio`, `silent`. + are `pushover`, `email`, `ntfy`, `apprise`, `pushbullet`, `slack`, `telegram`, `twilio`, `silent`. Defaults to `silent` - which just logs messages to console. [\*\*_example_](#send-a-push-notification) - `--equipment` @@ -436,6 +436,7 @@ camply supports notifications via a number of services: - [Pushover](https://pushover.net) - Email - [Ntfy](https://ntfy.sh) +- [Apprise](https://github.com/caronc/apprise) - [Pushbullet](https://www.pushbullet.com/#settings/account) - [Slack](https://slack.com) - [Telegram](https://core.telegram.org/bots) @@ -504,6 +505,19 @@ camply campsites \ --notifications pushover ``` +### Send a Notification Using Apprise-Compatible Services + +In addition to the built-in notification providers, you can also send notifications using one of the +many popular services supported by [Apprise](https://github.com/caronc/apprise). You'll need +to set up a supported notification URL using the `APPRISE_URL` config value; Refer to the Apprise +documentation to learn how to format a URL to fit your needs. + +Using Apprise notifications requires the `apprise` extras: + +``` +pip install camply[apprise] +``` + ### Searching for Specific Weekdays The below search spans across a three-month time window, but it only looks for campsites that diff --git a/docs/examples/example.camply b/docs/examples/example.camply index a52fb627..528c2def 100644 --- a/docs/examples/example.camply +++ b/docs/examples/example.camply @@ -26,6 +26,9 @@ EMAIL_SUBJECT_LINE="Camply Notification" # REQUIRED TO SEND NTFY NOTIFICATIONS NTFY_TOPIC="" +# REQUIRED TO SEND APPRISE NOTIFICATIONS +APPRISE_URL="" + # PERSONAL PUSHOVER API TOKEN (OPTIONAL) PUSHOVER_PUSH_TOKEN="" # PERSONAL RECREATION.GOV API KEY: https://ridb.recreation.gov/profile (OPTIONAL) diff --git a/docs/how_to_run.md b/docs/how_to_run.md index 3fcd4e19..ec798e5c 100644 --- a/docs/how_to_run.md +++ b/docs/how_to_run.md @@ -118,6 +118,8 @@ available. - Pushover Notifications - `PUSHOVER_PUSH_USER` +- Apprise Notifications + - `APPRISE_URL` - Email Notifications - `EMAIL_TO_ADDRESS` - `EMAIL_USERNAME` diff --git a/docs/index.md b/docs/index.md index f01af98f..1b026846 100644 --- a/docs/index.md +++ b/docs/index.md @@ -58,6 +58,7 @@ becomes available, camply sends you a notification to book your spot! - [Send a Text Message](command_line_usage.md#send-a-text-message) - [Send a Notification to Different Services](command_line_usage.md#send-a-notification-to-different-services) - [Searching for Specific Weekdays](command_line_usage.md#searching-for-specific-weekdays) + - [Send a Notification Using Apprise-Compatible Services](command_line_usage.md#send-a-notification-using-apprise-compatible-services) - [Look for Weekend Campsite Availabilities](command_line_usage.md#look-for-weekend-campsite-availabilities) - [Look for Consecutive Nights at the Same Campsite](command_line_usage.md#look-for-consecutive-nights-at-the-same-campsite) - [Look for a Campsite Inside of Yellowstone](command_line_usage.md#look-for-a-campsite-inside-of-yellowstone) diff --git a/pyproject.toml b/pyproject.toml index 3f7d9734..fa626795 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,8 +38,12 @@ requires-python = ">=3.8,<4" [project.optional-dependencies] all = [ + "apprise~=1.3.0", "twilio~=7.17.0" ] +apprise = [ + "apprise~=1.3.0" +] twilio = [ "twilio~=7.17.0" ] diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index c54d6272..57d4bb86 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,9 +1,13 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --all-extras --generate-hashes --output-file=requirements/requirements-dev.txt --resolver=backtracking # +apprise==1.3.0 \ + --hash=sha256:2c50c19a5dd41317b1f659c52b21a990febe6c15e08464228a1ce8e6098f11bf \ + --hash=sha256:ef0c1413a32182272b6ed60ddcee6057744dc683725651015332e063b9a6a4ad + # via camply (pyproject.toml) black==23.3.0 \ --hash=sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5 \ --hash=sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915 \ @@ -38,7 +42,9 @@ build==0.10.0 \ certifi==2022.12.7 \ --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 - # via requests + # via + # apprise + # requests charset-normalizer==3.1.0 \ --hash=sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6 \ --hash=sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1 \ @@ -121,6 +127,7 @@ click==8.1.3 \ --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48 # via # camply (pyproject.toml) + # apprise # black # mkdocs # mkdocs-click @@ -185,10 +192,6 @@ coverage[toml]==7.2.3 \ --hash=sha256:fd214917cabdd6f673a29d708574e9fbdb892cb77eb426d0eae3490d95ca7859 \ --hash=sha256:fff5aaa6becf2c6a1699ae6a39e2e6fb0672c2d42eca8eb0cafa91cf2e9bd312 # via pytest-cov -exceptiongroup==1.1.1 \ - --hash=sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e \ - --hash=sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785 - # via pytest execnet==1.9.0 \ --hash=sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5 \ --hash=sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142 @@ -211,16 +214,6 @@ idna==3.4 \ # via # requests # yarl -importlib-metadata==6.6.0 \ - --hash=sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed \ - --hash=sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705 - # via - # markdown - # mkdocs -importlib-resources==5.12.0 \ - --hash=sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6 \ - --hash=sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a - # via fake-useragent iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 @@ -236,6 +229,7 @@ markdown==3.3.7 \ --hash=sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874 \ --hash=sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621 # via + # apprise # mkdocs # mkdocs-autorefs # mkdocs-click @@ -497,6 +491,10 @@ numpy==1.24.2 \ --hash=sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780 \ --hash=sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa # via pandas +oauthlib==3.2.2 \ + --hash=sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca \ + --hash=sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918 + # via requests-oauthlib packaging==23.1 \ --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f @@ -697,6 +695,7 @@ pyyaml==6.0 \ --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 # via # camply (pyproject.toml) + # apprise # mkdocs # pymdown-extensions # pyyaml-env-tag @@ -775,8 +774,14 @@ requests==2.28.2 \ --hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf # via # camply (pyproject.toml) + # apprise # mkdocs-material + # requests-oauthlib # twilio +requests-oauthlib==1.3.1 \ + --hash=sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5 \ + --hash=sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a + # via apprise rich==13.3.4 \ --hash=sha256:22b74cae0278fd5086ff44144d3813be1cedc9115bdfabbfefd86400cb88b20a \ --hash=sha256:b5d573e13605423ec80bdd0cd5f8541f7844a0e71a13f74cf454ccb2f490708b @@ -816,16 +821,6 @@ tenacity==8.2.2 \ --hash=sha256:2f277afb21b851637e8f52e6a613ff08734c347dc19ade928e519d7d2d8569b0 \ --hash=sha256:43af037822bd0029025877f3b2d97cc4d7bb0c2991000a3d59d71517c5c969e0 # via camply (pyproject.toml) -tomli==2.0.1 \ - --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ - --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f - # via - # black - # build - # coverage - # mypy - # pyproject-hooks - # pytest twilio==7.17.0 \ --hash=sha256:312e25b0a1ca782974a359d95123f270574e6e4b195b0e2095a156219ad7b7ec \ --hash=sha256:f43de32cdc27f8853bb02ab0b8703a487beffbb9dcc8118398b6757e1376e6ed @@ -838,8 +833,6 @@ typing-extensions==4.5.0 \ --hash=sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb \ --hash=sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4 # via - # black - # mkdocstrings # mypy # pydantic urllib3==1.26.15 \ @@ -1036,12 +1029,6 @@ yarl==1.9.1 \ --hash=sha256:f878a78ed2ccfbd973cab46dd0933ecd704787724db23979e5731674d76eb36f \ --hash=sha256:f8e73f526140c1c32f5fca4cd0bc3b511a1abcd948f45b2a38a95e4edb76ca72 # via vcrpy -zipp==3.15.0 \ - --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ - --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 - # via - # importlib-metadata - # importlib-resources # WARNING: The following packages were not pinned, but pip requires them to be # pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag. diff --git a/requirements/requirements-prod.txt b/requirements/requirements-prod.txt index 65fd3102..956a20fb 100644 --- a/requirements/requirements-prod.txt +++ b/requirements/requirements-prod.txt @@ -1,13 +1,19 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --all-extras --generate-hashes --output-file=requirements/requirements-prod.txt --resolver=backtracking # +apprise==1.3.0 \ + --hash=sha256:2c50c19a5dd41317b1f659c52b21a990febe6c15e08464228a1ce8e6098f11bf \ + --hash=sha256:ef0c1413a32182272b6ed60ddcee6057744dc683725651015332e063b9a6a4ad + # via camply (pyproject.toml) certifi==2022.12.7 \ --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 - # via requests + # via + # apprise + # requests charset-normalizer==3.1.0 \ --hash=sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6 \ --hash=sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1 \ @@ -90,6 +96,7 @@ click==8.1.3 \ --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48 # via # camply (pyproject.toml) + # apprise # rich-click fake-useragent==1.1.3 \ --hash=sha256:1c06f0aa7d6e4894b919b30b9c7ebd72ff497325191057fbb5df3d5db06b93fc \ @@ -99,10 +106,10 @@ idna==3.4 \ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 # via requests -importlib-resources==5.12.0 \ - --hash=sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6 \ - --hash=sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a - # via fake-useragent +markdown==3.4.3 \ + --hash=sha256:065fd4df22da73a625f14890dd77eb8040edcbd68794bcd35943be14490608b2 \ + --hash=sha256:8bf101198e004dc93e84a12a7395e31aac6a9c9942848ae1d99b9d72cf9b3520 + # via apprise markdown-it-py==2.2.0 \ --hash=sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30 \ --hash=sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1 @@ -141,6 +148,10 @@ numpy==1.24.2 \ --hash=sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780 \ --hash=sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa # via pandas +oauthlib==3.2.2 \ + --hash=sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca \ + --hash=sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918 + # via requests-oauthlib pandas==1.5.3 \ --hash=sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813 \ --hash=sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792 \ @@ -272,7 +283,9 @@ pyyaml==6.0 \ --hash=sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f \ --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 - # via camply (pyproject.toml) + # via + # camply (pyproject.toml) + # apprise ratelimit==2.2.1 \ --hash=sha256:af8a9b64b821529aca09ebaf6d8d279100d766f19e90b5059ac6a718ca6dee42 # via camply (pyproject.toml) @@ -281,7 +294,13 @@ requests==2.28.2 \ --hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf # via # camply (pyproject.toml) + # apprise + # requests-oauthlib # twilio +requests-oauthlib==1.3.1 \ + --hash=sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5 \ + --hash=sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a + # via apprise rich==13.3.4 \ --hash=sha256:22b74cae0278fd5086ff44144d3813be1cedc9115bdfabbfefd86400cb88b20a \ --hash=sha256:b5d573e13605423ec80bdd0cd5f8541f7844a0e71a13f74cf454ccb2f490708b @@ -312,7 +331,3 @@ urllib3==1.26.15 \ --hash=sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305 \ --hash=sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42 # via requests -zipp==3.15.0 \ - --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ - --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 - # via importlib-resources