From fe9ebe7f65b540ac1ce7554588a3ea2357fe59a8 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 01:15:31 +0100 Subject: [PATCH 01/55] Fix name --- {.forjego => .forgejo}/workflows/build.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {.forjego => .forgejo}/workflows/build.yaml (100%) diff --git a/.forjego/workflows/build.yaml b/.forgejo/workflows/build.yaml similarity index 100% rename from .forjego/workflows/build.yaml rename to .forgejo/workflows/build.yaml From fbfe5ef643b296a09446d863b6734a393631b039 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 01:16:48 +0100 Subject: [PATCH 02/55] Fix action --- .forgejo/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 9f30435..8700416 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -17,7 +17,7 @@ jobs: fetch-depth: 0 - name: Install - run: uv sync + run: .venv/bin/uv sync - name: Run tests (API call) run: pytest -s tests/basic.py From fc899718c9dae241f67ce46ba90cfc327a9b9749 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 01:24:26 +0100 Subject: [PATCH 03/55] Install uv for the test action --- .forgejo/workflows/build.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 8700416..13aaa5e 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -16,8 +16,13 @@ jobs: with: fetch-depth: 0 + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v4 + with: + version: "latest" + - name: Install - run: .venv/bin/uv sync + run: uv sync - name: Run tests (API call) run: pytest -s tests/basic.py From 63aa8fe9ff9072fe1681080d34d5248a5b52b686 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 01:25:57 +0100 Subject: [PATCH 04/55] Install uv for the test action --- .forgejo/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 13aaa5e..4ae342a 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -17,7 +17,7 @@ jobs: fetch-depth: 0 - name: Install the latest version of uv - uses: astral-sh/setup-uv@v4 + uses: actions/setup-uv@v4 with: version: "latest" From 9cd72331aa9f77c75e4a26230832b6137921b90e Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 01:37:05 +0100 Subject: [PATCH 05/55] Fix install uv for the test action --- .forgejo/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 4ae342a..13aaa5e 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -17,7 +17,7 @@ jobs: fetch-depth: 0 - name: Install the latest version of uv - uses: actions/setup-uv@v4 + uses: astral-sh/setup-uv@v4 with: version: "latest" From 9d4bad339cd7abdac97bb3f12a0e6d2f6c90a038 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 01:41:27 +0100 Subject: [PATCH 06/55] Fix install uv for the test action --- .forgejo/workflows/build.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 13aaa5e..50a4a58 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -18,8 +18,6 @@ jobs: - name: Install the latest version of uv uses: astral-sh/setup-uv@v4 - with: - version: "latest" - name: Install run: uv sync From 4629e561cb413e158b89e3e4db28d08a4afaba55 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 02:52:07 +0100 Subject: [PATCH 07/55] Fix install uv for the test action --- .forgejo/workflows/build.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 50a4a58..60dddd0 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -18,6 +18,8 @@ jobs: - name: Install the latest version of uv uses: astral-sh/setup-uv@v4 + with: + version: "0.5.5" - name: Install run: uv sync From d58b36117ae679a8df3004aba1df1b97451988f7 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 03:01:42 +0100 Subject: [PATCH 08/55] Fix install uv for the test action --- .forgejo/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 60dddd0..2d140c8 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -25,4 +25,4 @@ jobs: run: uv sync - name: Run tests (API call) - run: pytest -s tests/basic.py + run: .venv/bin/pytest/pytest -s tests/basic.py From 0ada185e134c1dce3ca1db673b4009119331f0f5 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 03:02:11 +0100 Subject: [PATCH 09/55] Fix install uv for the test action --- .forgejo/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 2d140c8..b83a00b 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -25,4 +25,4 @@ jobs: run: uv sync - name: Run tests (API call) - run: .venv/bin/pytest/pytest -s tests/basic.py + run: .venv/bin/pytest -s tests/basic.py From 9a2dcd5a7708ee5423d2559e6a1486a3ec4feacf Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 03:11:11 +0100 Subject: [PATCH 10/55] Improve doc --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9d37d8c..e45329b 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,13 @@ apt install -y pipx && pipx install uv Configuration is done by environment variables, prefixed by `SMS_HANDLER_`: -* `MAIL_SENDER`: origin email address (default: @localhost) -* `MAIL_TO`: destination email address (default: @localhost) -* `MAIL_SERVER`: address of the mail server (default: localhost) -* `MAIL_SERVER_PORT`: port of the mail server (default: 25) -* `MAIL_SERVER_START_TLS`: initiate mail server connection with TLS (default: no) -* `MAIL_TEMLATE`: used to format the body of the mails -* `MAIL_ENABLE`: no or false to disable mails (default: yes) +* `SMS_HANDLER_MAIL_SENDER`: origin email address (default: @localhost) +* `SMS_HANDLER_MAIL_TO`: destination email address (default: @localhost) +* `SMS_HANDLER_MAIL_SERVER`: address of the mail server (default: localhost) +* `SMS_HANDLER_MAIL_SERVER_PORT`: port of the mail server (default: 25) +* `SMS_HANDLER_MAIL_SERVER_START_TLS`: initiate mail server connection with TLS (default: no) +* `SMS_HANDLER_MAIL_TEMLATE`: used to format the body of the mails +* `SMS_HANDLER_MAIL_ENABLE`: no or false to disable mails (default: yes) The default settings assume that a mail server runs on the localhost, and mails are sent to the user that owns the process. From 01ef6788230bb5b0428df1140c35f38ac8ca359a Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 04:58:49 +0100 Subject: [PATCH 11/55] Improve doc --- README.md | 95 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index e45329b..0941db3 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ apt install -y pipx && pipx install uv Configuration is done by environment variables, prefixed by `SMS_HANDLER_`: -* `SMS_HANDLER_MAIL_SENDER`: origin email address (default: @localhost) -* `SMS_HANDLER_MAIL_TO`: destination email address (default: @localhost) +* `SMS_HANDLER_MAIL_SENDER`: origin email address (default: *username*@localhost) +* `SMS_HANDLER_MAIL_TO`: destination email address (default: *username*@localhost) * `SMS_HANDLER_MAIL_SERVER`: address of the mail server (default: localhost) * `SMS_HANDLER_MAIL_SERVER_PORT`: port of the mail server (default: 25) * `SMS_HANDLER_MAIL_SERVER_START_TLS`: initiate mail server connection with TLS (default: no) @@ -34,14 +34,13 @@ The URI path `/sms-handle` is not configurable. ### In foreground ```sh -fastapi run server:app +sms-handler ``` -Try `fastapi --help` for configuration of the server: -port, address to listen on, etc. For example: +Set port and listen address: ```sh -fastapi run server:app --port 8025 +sms-handler --port 80125 --host 192.168.100.55 ``` ### As a deamon (Systemd) @@ -50,26 +49,30 @@ Create a service as follows. Pass the settings for the web server (listen address, port) in the ExecStart statement, and the parameters as `Environment=MAIL_SERVER_START_TLS=yes`. - [Unit] - Description=SMS handler - - [Service] - ExecStart=fastapi run server:app - Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com - Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com - Restart=on-failure - - [Install] - WantedBy=default.target +```systemd +[Unit] +Description=SMS handler + +[Service] +ExecStart=/home/snoopy/.local/bin/sms-handler +Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com +Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com +Restart=on-failure + +[Install] +WantedBy=default.target +``` ### Container Use `podman` or `docker`: +```sh podman run tiptop:5000/sms_handler - ``` +**Note**: the container is not published publicly yet. + Pass parameters like this: ```sh @@ -84,19 +87,21 @@ Systemd can "orchestrate" containers easily thanks to podman. For a rootless container, create a file in `.config/containers/systemd/sms-handler.container`: - [Container] - ContainerName=sms-handler - Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com - Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com - Image=tiptop:5000/sms_handler - Network=podman - PublishPort=80°25:8025 - - [Service] - Restart=always - - [Install] - WantedBy=default.target +```systemd +[Container] +ContainerName=sms-handler +Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com +Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com +Image=tiptop:5000/sms_handler +Network=podman +PublishPort=80°25:8025 + +[Service] +Restart=always + +[Install] +WantedBy=default.target +``` Request systemd to reload configuration, creating the service for this container: @@ -120,19 +125,21 @@ sudo loginctl enable-linger snoopy For a rootless container, create a file in `/etc/containers/systemd/sms-handler.container`: - [Container] - ContainerName=sms-handler - Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com - Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com - Image=tiptop:5000/sms_handler - Network=podman - PublishPort=80°25:8025 - - [Service] - Restart=always - - [Install] - WantedBy=default.target +```systemd +[Container] +ContainerName=sms-handler +Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com +Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com +Image=tiptop:5000/sms_handler +Network=podman +PublishPort=80°25:8025 + +[Service] +Restart=always + +[Install] +WantedBy=default.target +``` Request systemd to reload configuration, creating the service for this container: From 9558e45bed6bd20a00156e146611d16fb1924544 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 05:01:45 +0100 Subject: [PATCH 12/55] Slim down requirements --- pyproject.toml | 3 +- src/sms_handler/server.py | 8 +- uv.lock | 421 +------------------------------------- 3 files changed, 13 insertions(+), 419 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 38ca1c3..faa09ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,8 +8,9 @@ authors = [{ name = "phil", email = "phil.dev@philome.mooo.com" }] requires-python = ">=3.11" dependencies = [ "aiosmtplib>=3.0.2", - "fastapi[standard]>=0.115.5", + "fastapi>=0.115.5", "pydantic-settings>=2.6.1", + "uvicorn>=0.32.1", ] [project.scripts] diff --git a/src/sms_handler/server.py b/src/sms_handler/server.py index 58d8fc0..aeceefc 100755 --- a/src/sms_handler/server.py +++ b/src/sms_handler/server.py @@ -81,5 +81,11 @@ async def handle_sms(smsAlert: SmsAlert): def main(): from uvicorn import run + from argparse import ArgumentParser - run(app, host="0.0.0.0", port=8025) + parser = ArgumentParser(description=__doc__) + parser.add_argument("-l", "--host", default="0.0.0.0") + parser.add_argument("-p", "--port", type=int, default=8025) + args = parser.parse_args() + + run(app, host=args.host, port=args.port) diff --git a/uv.lock b/uv.lock index 70ee7fc..1af3c28 100644 --- a/uv.lock +++ b/uv.lock @@ -32,15 +32,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 }, ] -[[package]] -name = "certifi" -version = "2024.8.30" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, -] - [[package]] name = "click" version = "8.1.7" @@ -62,28 +53,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] -[[package]] -name = "dnspython" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 }, -] - -[[package]] -name = "email-validator" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dnspython" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 }, -] - [[package]] name = "fastapi" version = "0.115.5" @@ -98,34 +67,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/c4/148d5046a96c428464557264877ae5a9338a83bbe0df045088749ec89820/fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796", size = 94866 }, ] -[package.optional-dependencies] -standard = [ - { name = "email-validator" }, - { name = "fastapi-cli", extra = ["standard"] }, - { name = "httpx" }, - { name = "jinja2" }, - { name = "python-multipart" }, - { name = "uvicorn", extra = ["standard"] }, -] - -[[package]] -name = "fastapi-cli" -version = "0.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typer" }, - { name = "uvicorn", extra = ["standard"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/f8/1ad5ce32d029aeb9117e9a5a9b3e314a8477525d60c12a9b7730a3c186ec/fastapi_cli-0.0.5.tar.gz", hash = "sha256:d30e1239c6f46fcb95e606f02cdda59a1e2fa778a54b64686b3ff27f6211ff9f", size = 15571 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/ea/4b5011012ac925fe2f83b19d0e09cee9d324141ec7bf5e78bb2817f96513/fastapi_cli-0.0.5-py3-none-any.whl", hash = "sha256:e94d847524648c748a5350673546bbf9bcaeb086b33c24f2e82e021436866a46", size = 9489 }, -] - -[package.optional-dependencies] -standard = [ - { name = "uvicorn", extra = ["standard"] }, -] - [[package]] name = "h11" version = "0.14.0" @@ -135,63 +76,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, ] -[[package]] -name = "httpcore" -version = "1.0.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, -] - -[[package]] -name = "httptools" -version = "0.6.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029 }, - { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492 }, - { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891 }, - { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788 }, - { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214 }, - { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120 }, - { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565 }, - { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683 }, - { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337 }, - { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796 }, - { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837 }, - { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289 }, - { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779 }, - { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634 }, - { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214 }, - { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431 }, - { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121 }, - { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805 }, - { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858 }, - { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042 }, - { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682 }, -] - -[[package]] -name = "httpx" -version = "0.28.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/10/df/676b7cf674dd1bdc71a64ad393c89879f75e4a0ab8395165b498262ae106/httpx-0.28.0.tar.gz", hash = "sha256:0858d3bab51ba7e386637f22a61d8ccddaeec5f3fe4209da3a6168dbb91573e0", size = 141307 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/fb/a19866137577ba60c6d8b69498dc36be479b13ba454f691348ddf428f185/httpx-0.28.0-py3-none-any.whl", hash = "sha256:dc0b419a0cfeb6e8b34e85167c0da2671206f5095f1baa9663d23bcfd6b535fc", size = 73551 }, -] - [[package]] name = "idna" version = "3.10" @@ -210,87 +94,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, ] -[[package]] -name = "jinja2" -version = "3.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, -] - -[[package]] -name = "markupsafe" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, - { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, - { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, - { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, - { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, - { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, - { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, - { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, - { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, -] - [[package]] name = "packaging" version = "24.2" @@ -389,15 +192,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/f9/ff95fd7d760af42f647ea87f9b8a383d891cdb5e5dbd4613edaeb094252a/pydantic_settings-2.6.1-py3-none-any.whl", hash = "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87", size = 28595 }, ] -[[package]] -name = "pygments" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, -] - [[package]] name = "pytest" version = "8.3.3" @@ -422,80 +216,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, ] -[[package]] -name = "python-multipart" -version = "0.0.18" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b4/86/b6b38677dec2e2e7898fc5b6f7e42c2d011919a92d25339451892f27b89c/python_multipart-0.0.18.tar.gz", hash = "sha256:7a68db60c8bfb82e460637fa4750727b45af1d5e2ed215593f917f64694d34fe", size = 36622 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/6b/b60f47101ba2cac66b4a83246630e68ae9bbe2e614cbae5f4465f46dee13/python_multipart-0.0.18-py3-none-any.whl", hash = "sha256:efe91480f485f6a361427a541db4796f9e1591afc0fb8e7a4ba06bfbc6708996", size = 24389 }, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, -] - -[[package]] -name = "rich" -version = "13.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, -] - [[package]] name = "sms-handler" version = "0.0.0" source = { editable = "." } dependencies = [ { name = "aiosmtplib" }, - { name = "fastapi", extra = ["standard"] }, + { name = "fastapi" }, { name = "pydantic-settings" }, + { name = "uvicorn" }, ] [package.dev-dependencies] @@ -506,8 +235,9 @@ dev = [ [package.metadata] requires-dist = [ { name = "aiosmtplib", specifier = ">=3.0.2" }, - { name = "fastapi", extras = ["standard"], specifier = ">=0.115.5" }, + { name = "fastapi", specifier = ">=0.115.5" }, { name = "pydantic-settings", specifier = ">=2.6.1" }, + { name = "uvicorn", specifier = ">=0.32.1" }, ] [package.metadata.requires-dev] @@ -534,21 +264,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/00/2b325970b3060c7cecebab6d295afe763365822b1306a12eeab198f74323/starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7", size = 73225 }, ] -[[package]] -name = "typer" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0d/7e/24af5b9aaa0872f9f6dc5dcf789dc3e57ceb23b4c570b852cd4db0d98f14/typer-0.14.0.tar.gz", hash = "sha256:af58f737f8d0c0c37b9f955a6d39000b9ff97813afcbeef56af5e37cf743b45a", size = 98836 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/d8/a3ab71d5587b42b832a7ef2e65b3e51a18f8da32b6ce169637d4d21995ed/typer-0.14.0-py3-none-any.whl", hash = "sha256:f476233a25770ab3e7b2eebf7c68f3bc702031681a008b20167573a4b7018f09", size = 44707 }, -] - [[package]] name = "typing-extensions" version = "4.12.2" @@ -570,131 +285,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/6a/3c/21dba3e7d76138725 wheels = [ { url = "https://files.pythonhosted.org/packages/50/c1/2d27b0a15826c2b71dcf6e2f5402181ef85acf439617bb2f1453125ce1f3/uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e", size = 63828 }, ] - -[package.optional-dependencies] -standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "httptools" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, - { name = "watchfiles" }, - { name = "websockets" }, -] - -[[package]] -name = "uvloop" -version = "0.21.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/a7/4cf0334105c1160dd6819f3297f8700fda7fc30ab4f61fbf3e725acbc7cc/uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8", size = 1447410 }, - { url = "https://files.pythonhosted.org/packages/8c/7c/1517b0bbc2dbe784b563d6ab54f2ef88c890fdad77232c98ed490aa07132/uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0", size = 805476 }, - { url = "https://files.pythonhosted.org/packages/ee/ea/0bfae1aceb82a503f358d8d2fa126ca9dbdb2ba9c7866974faec1cb5875c/uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e", size = 3960855 }, - { url = "https://files.pythonhosted.org/packages/8a/ca/0864176a649838b838f36d44bf31c451597ab363b60dc9e09c9630619d41/uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb", size = 3973185 }, - { url = "https://files.pythonhosted.org/packages/30/bf/08ad29979a936d63787ba47a540de2132169f140d54aa25bc8c3df3e67f4/uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6", size = 3820256 }, - { url = "https://files.pythonhosted.org/packages/da/e2/5cf6ef37e3daf2f06e651aae5ea108ad30df3cb269102678b61ebf1fdf42/uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d", size = 3937323 }, - { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284 }, - { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349 }, - { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089 }, - { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770 }, - { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321 }, - { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022 }, - { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123 }, - { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325 }, - { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806 }, - { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068 }, - { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428 }, - { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018 }, -] - -[[package]] -name = "watchfiles" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/5e/5a9dfb8594b075d7c225d5fb628d498001c5dfae62298e9eb85b8754668f/watchfiles-1.0.0.tar.gz", hash = "sha256:37566c844c9ce3b5deb964fe1a23378e575e74b114618d211fbda8f59d7b5dab", size = 38187 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/10/10759faea3f011b86867a534a47c9aedca667a4b3806ffeac7d8a4c8adee/watchfiles-1.0.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:fbd0ab7a9943bbddb87cbc2bf2f09317e74c77dc55b1f5657f81d04666c25269", size = 394139 }, - { url = "https://files.pythonhosted.org/packages/b9/71/b76be784f3e48bb1929e2c1376f227608be9bda4f7ba0c06832f0d190bed/watchfiles-1.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:774ef36b16b7198669ce655d4f75b4c3d370e7f1cbdfb997fb10ee98717e2058", size = 382832 }, - { url = "https://files.pythonhosted.org/packages/d6/88/393b33c6da4963933e810eb0b8d6b44c7ba52ed2aaf6bb7709db377289f8/watchfiles-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b4fb98100267e6a5ebaff6aaa5d20aea20240584647470be39fe4823012ac96", size = 441232 }, - { url = "https://files.pythonhosted.org/packages/35/2c/2d2c131866f7c49ec68c504565d2336f40a595bcd857cd464a68ea0fdb42/watchfiles-1.0.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0fc3bf0effa2d8075b70badfdd7fb839d7aa9cea650d17886982840d71fdeabf", size = 447569 }, - { url = "https://files.pythonhosted.org/packages/ab/08/373713cc4859958cdf0a38ad85740010dbbf5617441edc3480d37387024c/watchfiles-1.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:648e2b6db53eca6ef31245805cd528a16f56fa4cc15aeec97795eaf713c11435", size = 472439 }, - { url = "https://files.pythonhosted.org/packages/2b/df/8e209910e260f58f005974a60423bb6fc243d26e8793103462870502c744/watchfiles-1.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa13d604fcb9417ae5f2e3de676e66aa97427d888e83662ad205bed35a313176", size = 492707 }, - { url = "https://files.pythonhosted.org/packages/83/4d/d0673571c223a784849f45c4da6de2af960602ba5061a2f033f96606a118/watchfiles-1.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:936f362e7ff28311b16f0b97ec51e8f2cc451763a3264640c6ed40fb252d1ee4", size = 489294 }, - { url = "https://files.pythonhosted.org/packages/32/ed/0c96c714408c8edab862e816b45be51dbe4e77dc7518c29b0dccc02961a8/watchfiles-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:245fab124b9faf58430da547512d91734858df13f2ddd48ecfa5e493455ffccb", size = 442559 }, - { url = "https://files.pythonhosted.org/packages/3d/2b/665bf9aefd0f22a265f7b93e69aa4dc068d8ac5ad9ecbd974305eaeff2c0/watchfiles-1.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4ff9c7e84e8b644a8f985c42bcc81457240316f900fc72769aaedec9d088055a", size = 614531 }, - { url = "https://files.pythonhosted.org/packages/9f/41/fd125e824a195219adb204b54f3affce5615f5f1b3889acd441f28d2fbd2/watchfiles-1.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9c9a8d8fd97defe935ef8dd53d562e68942ad65067cd1c54d6ed8a088b1d931d", size = 612852 }, - { url = "https://files.pythonhosted.org/packages/dc/ac/750bf3625f4d3172ee7acfd952552070a88fd697935cfead79a68eb8d69d/watchfiles-1.0.0-cp311-none-win32.whl", hash = "sha256:a0abf173975eb9dd17bb14c191ee79999e650997cc644562f91df06060610e62", size = 272294 }, - { url = "https://files.pythonhosted.org/packages/bd/04/8c18986b79d106a88f54629f8f901cd725d76227c9a9191ada8ce8c962e8/watchfiles-1.0.0-cp311-none-win_amd64.whl", hash = "sha256:2a825ba4b32c214e3855b536eb1a1f7b006511d8e64b8215aac06eb680642d84", size = 285435 }, - { url = "https://files.pythonhosted.org/packages/b4/38/7e64929e8ca2b2a94cb9d8ddf6be9c06be8be870b6014d0f06e76b72f9cf/watchfiles-1.0.0-cp311-none-win_arm64.whl", hash = "sha256:a5a7a06cfc65e34fd0a765a7623c5ba14707a0870703888e51d3d67107589817", size = 276512 }, - { url = "https://files.pythonhosted.org/packages/37/0a/75491ba001f1495d2a12d7f6b90738f20badac78291ca5d56bf7990c859a/watchfiles-1.0.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:28fb64b5843d94e2c2483f7b024a1280662a44409bedee8f2f51439767e2d107", size = 394139 }, - { url = "https://files.pythonhosted.org/packages/5a/ee/935095538ff08ab68555de2bbc18acaf91f4cce8518bf32196f1ff9b8326/watchfiles-1.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e3750434c83b61abb3163b49c64b04180b85b4dabb29a294513faec57f2ffdb7", size = 382832 }, - { url = "https://files.pythonhosted.org/packages/74/40/86787dca3ea251aabb3abfbe4beeffe9c7ae6e69de56a25d572aecde580e/watchfiles-1.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bedf84835069f51c7b026b3ca04e2e747ea8ed0a77c72006172c72d28c9f69fc", size = 441232 }, - { url = "https://files.pythonhosted.org/packages/59/e2/08db1ba48a30462ec7e382c2b1de5400b09a2a7c95fe3f16d3e7da844f0c/watchfiles-1.0.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:90004553be36427c3d06ec75b804233f8f816374165d5225b93abd94ba6e7234", size = 447569 }, - { url = "https://files.pythonhosted.org/packages/73/54/10adf42f203d876076cf0684726c102b3dba82b1c7eea2d82e5991875f62/watchfiles-1.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b46e15c34d4e401e976d6949ad3a74d244600d5c4b88c827a3fdf18691a46359", size = 472439 }, - { url = "https://files.pythonhosted.org/packages/29/77/d0d3b5ec6224800cd77f5d058473d0a844d753a3dad9f53f369bc98946bc/watchfiles-1.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:487d15927f1b0bd24e7df921913399bb1ab94424c386bea8b267754d698f8f0e", size = 492707 }, - { url = "https://files.pythonhosted.org/packages/c8/74/616bd8edfa7b0aaee96e4b3ad7edd0ccf0f4213a06050e965d68e0cdbaef/watchfiles-1.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ff236d7a3f4b0a42f699a22fc374ba526bc55048a70cbb299661158e1bb5e1f", size = 489293 }, - { url = "https://files.pythonhosted.org/packages/9c/1e/5335eaf5fb9a9516722c7f63f477ca1e361d8159fe46e03d96539cb80f5b/watchfiles-1.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c01446626574561756067f00b37e6b09c8622b0fc1e9fdbc7cbcea328d4e514", size = 442559 }, - { url = "https://files.pythonhosted.org/packages/c7/1c/df716e9acf7931b52f48bd9b2eec9a26ff55c73b43bfdbc03ea985543d01/watchfiles-1.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b551c465a59596f3d08170bd7e1c532c7260dd90ed8135778038e13c5d48aa81", size = 614531 }, - { url = "https://files.pythonhosted.org/packages/8d/38/c97d572e147234dd5f107179854efbf9ac6470db11db96f690cdb80e9b1b/watchfiles-1.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1ed613ee107269f66c2df631ec0fc8efddacface85314d392a4131abe299f00", size = 612853 }, - { url = "https://files.pythonhosted.org/packages/2d/1d/161eb1caa7e63b60428b2439efb0a87f0db4d5f4b91dd8712b6eca689954/watchfiles-1.0.0-cp312-none-win32.whl", hash = "sha256:5f75cd42e7e2254117cf37ff0e68c5b3f36c14543756b2da621408349bd9ca7c", size = 272337 }, - { url = "https://files.pythonhosted.org/packages/fc/1d/62acefeb546d24971e8f77cf5c475307054da4c21e9c49ec1917b293368b/watchfiles-1.0.0-cp312-none-win_amd64.whl", hash = "sha256:cf517701a4a872417f4e02a136e929537743461f9ec6cdb8184d9a04f4843545", size = 285572 }, - { url = "https://files.pythonhosted.org/packages/41/08/e20f3dbd2db59067596acc9b81345ac68a9c762352d38e789b2516719876/watchfiles-1.0.0-cp312-none-win_arm64.whl", hash = "sha256:8a2127cd68950787ee36753e6d401c8ea368f73beaeb8e54df5516a06d1ecd82", size = 276513 }, - { url = "https://files.pythonhosted.org/packages/c6/14/e14eb2ad369b306be70423fbf6da47bc39333d2beeafb14f23d2f37fdd79/watchfiles-1.0.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:95de85c254f7fe8cbdf104731f7f87f7f73ae229493bebca3722583160e6b152", size = 394141 }, - { url = "https://files.pythonhosted.org/packages/81/c3/738aeb2a01cbdf5fa823f702694ac72879a97fa5873d15d4607a877c7082/watchfiles-1.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:533a7cbfe700e09780bb31c06189e39c65f06c7f447326fee707fd02f9a6e945", size = 382833 }, - { url = "https://files.pythonhosted.org/packages/ed/aa/1cc14d11be667eb7189a2daa0adf307b93d6624fee5b80b8e84c23fb2486/watchfiles-1.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2218e78e2c6c07b1634a550095ac2a429026b2d5cbcd49a594f893f2bb8c936", size = 441231 }, - { url = "https://files.pythonhosted.org/packages/c5/38/96f4c3485094a164ced67ae444f3e890bdaad17d1b62c894aa8439443d81/watchfiles-1.0.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9122b8fdadc5b341315d255ab51d04893f417df4e6c1743b0aac8bf34e96e025", size = 447570 }, - { url = "https://files.pythonhosted.org/packages/9e/ce/0e35e0191517fa1d876ce0b4e23c818cf3a50d825305dcb7471da8774da7/watchfiles-1.0.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9272fdbc0e9870dac3b505bce1466d386b4d8d6d2bacf405e603108d50446940", size = 472440 }, - { url = "https://files.pythonhosted.org/packages/2c/b5/eb9c799c6e14f25f26573ac08734225035a8821f7dd9161c69df882fc119/watchfiles-1.0.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a3b33c3aefe9067ebd87846806cd5fc0b017ab70d628aaff077ab9abf4d06b3", size = 492706 }, - { url = "https://files.pythonhosted.org/packages/84/fa/985d4cbfe99a56d7277c0e522fd138fe5fc4d8ea6351ee3302e93ed67e63/watchfiles-1.0.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc338ce9f8846543d428260fa0f9a716626963148edc937d71055d01d81e1525", size = 489295 }, - { url = "https://files.pythonhosted.org/packages/94/1a/8bc18a170eb621a30fb01f4902d60ce362c88b1f65f3b15d45f53b467200/watchfiles-1.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ac778a460ea22d63c7e6fb0bc0f5b16780ff0b128f7f06e57aaec63bd339285", size = 442560 }, - { url = "https://files.pythonhosted.org/packages/e9/e0/07ce46f1770ca1d229635efb5393ff593c41762f389532ae9c7b2ced79b0/watchfiles-1.0.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:53ae447f06f8f29f5ab40140f19abdab822387a7c426a369eb42184b021e97eb", size = 614532 }, - { url = "https://files.pythonhosted.org/packages/7b/56/cdd2847d24249e879a001e6aed9ddeeaa24a80aabfdcb9c19389d0837dfe/watchfiles-1.0.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1f73c2147a453315d672c1ad907abe6d40324e34a185b51e15624bc793f93cc6", size = 612852 }, - { url = "https://files.pythonhosted.org/packages/72/c9/89a3df27c97eeef5890591a95f7afd266a32dfe55bce1f3bea3390fa56f5/watchfiles-1.0.0-cp313-none-win32.whl", hash = "sha256:eba98901a2eab909dbd79681190b9049acc650f6111fde1845484a4450761e98", size = 271721 }, - { url = "https://files.pythonhosted.org/packages/ef/e9/6e1bd83a08d254b0394500a2bb691b7940f09fcd849f400d01491932f641/watchfiles-1.0.0-cp313-none-win_amd64.whl", hash = "sha256:d562a6114ddafb09c33246c6ace7effa71ca4b6a2324a47f4b09b6445ea78941", size = 284809 }, -] - -[[package]] -name = "websockets" -version = "14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/1b/380b883ce05bb5f45a905b61790319a28958a9ab1e4b6b95ff5464b60ca1/websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8", size = 162840 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/ed/c0d03cb607b7fe1f7ff45e2cd4bb5cd0f9e3299ced79c2c303a6fff44524/websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512", size = 161949 }, - { url = "https://files.pythonhosted.org/packages/06/91/bf0a44e238660d37a2dda1b4896235d20c29a2d0450f3a46cd688f43b239/websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac", size = 159606 }, - { url = "https://files.pythonhosted.org/packages/ff/b8/7185212adad274c2b42b6a24e1ee6b916b7809ed611cbebc33b227e5c215/websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280", size = 159854 }, - { url = "https://files.pythonhosted.org/packages/5a/8a/0849968d83474be89c183d8ae8dcb7f7ada1a3c24f4d2a0d7333c231a2c3/websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1", size = 169402 }, - { url = "https://files.pythonhosted.org/packages/bd/4f/ef886e37245ff6b4a736a09b8468dae05d5d5c99de1357f840d54c6f297d/websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3", size = 168406 }, - { url = "https://files.pythonhosted.org/packages/11/43/e2dbd4401a63e409cebddedc1b63b9834de42f51b3c84db885469e9bdcef/websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6", size = 168776 }, - { url = "https://files.pythonhosted.org/packages/6d/d6/7063e3f5c1b612e9f70faae20ebaeb2e684ffa36cb959eb0862ee2809b32/websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0", size = 169083 }, - { url = "https://files.pythonhosted.org/packages/49/69/e6f3d953f2fa0f8a723cf18cd011d52733bd7f6e045122b24e0e7f49f9b0/websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89", size = 168529 }, - { url = "https://files.pythonhosted.org/packages/70/ff/f31fa14561fc1d7b8663b0ed719996cf1f581abee32c8fb2f295a472f268/websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23", size = 168475 }, - { url = "https://files.pythonhosted.org/packages/f1/15/b72be0e4bf32ff373aa5baef46a4c7521b8ea93ad8b49ca8c6e8e764c083/websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e", size = 162833 }, - { url = "https://files.pythonhosted.org/packages/bc/ef/2d81679acbe7057ffe2308d422f744497b52009ea8bab34b6d74a2657d1d/websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09", size = 163263 }, - { url = "https://files.pythonhosted.org/packages/55/64/55698544ce29e877c9188f1aee9093712411a8fc9732cca14985e49a8e9c/websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed", size = 161957 }, - { url = "https://files.pythonhosted.org/packages/a2/b1/b088f67c2b365f2c86c7b48edb8848ac27e508caf910a9d9d831b2f343cb/websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d", size = 159620 }, - { url = "https://files.pythonhosted.org/packages/c1/89/2a09db1bbb40ba967a1b8225b07b7df89fea44f06de9365f17f684d0f7e6/websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707", size = 159852 }, - { url = "https://files.pythonhosted.org/packages/ca/c1/f983138cd56e7d3079f1966e81f77ce6643f230cd309f73aa156bb181749/websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a", size = 169675 }, - { url = "https://files.pythonhosted.org/packages/c1/c8/84191455d8660e2a0bdb33878d4ee5dfa4a2cedbcdc88bbd097303b65bfa/websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45", size = 168619 }, - { url = "https://files.pythonhosted.org/packages/8d/a7/62e551fdcd7d44ea74a006dc193aba370505278ad76efd938664531ce9d6/websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58", size = 169042 }, - { url = "https://files.pythonhosted.org/packages/ad/ed/1532786f55922c1e9c4d329608e36a15fdab186def3ca9eb10d7465bc1cc/websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058", size = 169345 }, - { url = "https://files.pythonhosted.org/packages/ea/fb/160f66960d495df3de63d9bcff78e1b42545b2a123cc611950ffe6468016/websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4", size = 168725 }, - { url = "https://files.pythonhosted.org/packages/cf/53/1bf0c06618b5ac35f1d7906444b9958f8485682ab0ea40dee7b17a32da1e/websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05", size = 168712 }, - { url = "https://files.pythonhosted.org/packages/e5/22/5ec2f39fff75f44aa626f86fa7f20594524a447d9c3be94d8482cd5572ef/websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0", size = 162838 }, - { url = "https://files.pythonhosted.org/packages/74/27/28f07df09f2983178db7bf6c9cccc847205d2b92ced986cd79565d68af4f/websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f", size = 163277 }, - { url = "https://files.pythonhosted.org/packages/34/77/812b3ba5110ed8726eddf9257ab55ce9e85d97d4aa016805fdbecc5e5d48/websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9", size = 161966 }, - { url = "https://files.pythonhosted.org/packages/8d/24/4fcb7aa6986ae7d9f6d083d9d53d580af1483c5ec24bdec0978307a0f6ac/websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b", size = 159625 }, - { url = "https://files.pythonhosted.org/packages/f8/47/2a0a3a2fc4965ff5b9ce9324d63220156bd8bedf7f90824ab92a822e65fd/websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3", size = 159857 }, - { url = "https://files.pythonhosted.org/packages/dd/c8/d7b425011a15e35e17757e4df75b25e1d0df64c0c315a44550454eaf88fc/websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59", size = 169635 }, - { url = "https://files.pythonhosted.org/packages/93/39/6e3b5cffa11036c40bd2f13aba2e8e691ab2e01595532c46437b56575678/websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2", size = 168578 }, - { url = "https://files.pythonhosted.org/packages/cf/03/8faa5c9576299b2adf34dcccf278fc6bbbcda8a3efcc4d817369026be421/websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da", size = 169018 }, - { url = "https://files.pythonhosted.org/packages/8c/05/ea1fec05cc3a60defcdf0bb9f760c3c6bd2dd2710eff7ac7f891864a22ba/websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9", size = 169383 }, - { url = "https://files.pythonhosted.org/packages/21/1d/eac1d9ed787f80754e51228e78855f879ede1172c8b6185aca8cef494911/websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7", size = 168773 }, - { url = "https://files.pythonhosted.org/packages/0e/1b/e808685530185915299740d82b3a4af3f2b44e56ccf4389397c7a5d95d39/websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a", size = 168757 }, - { url = "https://files.pythonhosted.org/packages/b6/19/6ab716d02a3b068fbbeb6face8a7423156e12c446975312f1c7c0f4badab/websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6", size = 162834 }, - { url = "https://files.pythonhosted.org/packages/6c/fd/ab6b7676ba712f2fc89d1347a4b5bdc6aa130de10404071f2b2606450209/websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0", size = 163277 }, - { url = "https://files.pythonhosted.org/packages/b0/0b/c7e5d11020242984d9d37990310520ed663b942333b83a033c2f20191113/websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e", size = 156277 }, -] From 1844c06f98638460b689cede826925efa4327675 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 05:03:06 +0100 Subject: [PATCH 13/55] Fix CI test --- pyproject.toml | 5 ++++- uv.lock | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index faa09ca..7522915 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,4 +25,7 @@ packages = ["src/sms_handler"] [tool.uv] package = true -dev-dependencies = ["pytest>=8.3.3"] +dev-dependencies = [ + "httpx>=0.28.0", + "pytest>=8.3.3", +] diff --git a/uv.lock b/uv.lock index 1af3c28..b81d553 100644 --- a/uv.lock +++ b/uv.lock @@ -32,6 +32,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 }, ] +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + [[package]] name = "click" version = "8.1.7" @@ -76,6 +85,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, ] +[[package]] +name = "httpcore" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, +] + +[[package]] +name = "httpx" +version = "0.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/df/676b7cf674dd1bdc71a64ad393c89879f75e4a0ab8395165b498262ae106/httpx-0.28.0.tar.gz", hash = "sha256:0858d3bab51ba7e386637f22a61d8ccddaeec5f3fe4209da3a6168dbb91573e0", size = 141307 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/fb/a19866137577ba60c6d8b69498dc36be479b13ba454f691348ddf428f185/httpx-0.28.0-py3-none-any.whl", hash = "sha256:dc0b419a0cfeb6e8b34e85167c0da2671206f5095f1baa9663d23bcfd6b535fc", size = 73551 }, +] + [[package]] name = "idna" version = "3.10" @@ -229,6 +266,7 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "httpx" }, { name = "pytest" }, ] @@ -241,7 +279,10 @@ requires-dist = [ ] [package.metadata.requires-dev] -dev = [{ name = "pytest", specifier = ">=8.3.3" }] +dev = [ + { name = "httpx", specifier = ">=0.28.0" }, + { name = "pytest", specifier = ">=8.3.3" }, +] [[package]] name = "sniffio" From 00c84184f9cebbd16d64ef7c17f6899c96efcf24 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 04:38:48 +0000 Subject: [PATCH 14/55] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 0941db3..374778a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -Listen to messages from the SMS Forwarder app on Android and send mail. - ## Installation Use `uv` (). From e72f65ab8feec0f6e08b146e3453c43de9bf560a Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 04:40:37 +0000 Subject: [PATCH 15/55] Update Containerfile --- Containerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Containerfile b/Containerfile index ec18c10..27b8457 100644 --- a/Containerfile +++ b/Containerfile @@ -1,4 +1,4 @@ -# Build: podman build -t treetrail-backend-base -f Containerfile.base +# Build: podman build -t sms-handler -f Containerfile FROM docker.io/python:3.12-alpine COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/ From c3888238839cfafc745c741b862814115269afe9 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 06:03:11 +0100 Subject: [PATCH 16/55] Update README --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 374778a..e2a7d99 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@ ## Installation -Use `uv` (). -Easy way to install `uv` on Debian: +```sh +PIP_EXTRA_INDEX_URL=https://pypi.org/simple/ pip install --index-url https://philo.ydns.eu/code/api/packages/Philome/pypi/simple/ sms-handler +``` + +Debian and other systems require using `pipx` `pip`: ```sh -apt install -y pipx && pipx install uv +sudo apt install -y pipx +PIP_EXTRA_INDEX_URL=https://pypi.org/simple/ pipx install --index-url https://philo.ydns.eu/code/api/packages/Philome/pypi/simple/ sms-handler ``` ## Configuration @@ -69,7 +73,7 @@ Use `podman` or `docker`: podman run tiptop:5000/sms_handler ``` -**Note**: the container is not published publicly yet. +**Note**: the container is not published publicly yet! Pass parameters like this: From b4cfb5dc6b8f07bc0f0b9fb0c3f49b5733dab459 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 05:04:24 +0000 Subject: [PATCH 17/55] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2a7d99..28d4943 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com Image=tiptop:5000/sms_handler Network=podman -PublishPort=80°25:8025 +PublishPort=8025:8025 [Service] Restart=always From 0ac32cb47c0b213a2c02334484f308deeeda64f8 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 05:05:35 +0000 Subject: [PATCH 18/55] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 28d4943..cac3f40 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com Image=tiptop:5000/sms_handler Network=podman -PublishPort=80°25:8025 +PublishPort=8025:8025 [Service] Restart=always From 6a581eae0a07e095cc81b9e4b13063cc3edd3a84 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 13:12:34 +0100 Subject: [PATCH 19/55] Update README --- CONTAINER.md | 90 ++++++++++++++++++++++++++++++++++++++ README.md | 119 +++++++++++++-------------------------------------- 2 files changed, 119 insertions(+), 90 deletions(-) create mode 100644 CONTAINER.md diff --git a/CONTAINER.md b/CONTAINER.md new file mode 100644 index 0000000..546c8d3 --- /dev/null +++ b/CONTAINER.md @@ -0,0 +1,90 @@ +# Container + +Use `podman` or `docker`: + +```sh +podman run tiptop:5000/sms_handler +``` + +**Note**: the container is not published publicly yet! + +Pass parameters like this: + +```sh +podman run -e SMS_HANDLER_MAIL_SERVER_PORT=8025 -e SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com SMS_HANDLER_MAIL_TO=snoopy@peanuts.com tiptop:5000/sms_handler +``` + +## Container as a systemd service + +Systemd can "orchestrate" containers easily thanks to podman. + +### Rootless (as regular user) + +For a rootless container, create a file in `.config/containers/systemd/sms-handler.container`: + +```systemd +[Container] +ContainerName=sms-handler +Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com +Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com +Image=tiptop:5000/sms_handler +Network=podman +PublishPort=8025:8025 + +[Service] +Restart=always + +[Install] +WantedBy=default.target +``` + +Request systemd to reload configuration, creating the service for this container: + +```sh +systemctl --user daemon-reload +``` + +Start the container as a service: + +```sh +systemctl start sms-handler.service +``` + +To start rootless containers at boot time, without the need for the user (snoopy) to log in: + +```sh +sudo loginctl enable-linger snoopy +``` + +### Rootful (as root) + +For a rootless container, create a file in `/etc/containers/systemd/sms-handler.container`: + +```systemd +[Container] +ContainerName=sms-handler +Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com +Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com +Image=tiptop:5000/sms_handler +Network=podman +PublishPort=8025:8025 + +[Service] +Restart=always + +[Install] +WantedBy=default.target +``` + +Request systemd to reload configuration, creating the service for this container: + +```sh +sudo systemctl daemon-reload +``` + +Start the container as a service: + +```sh +sudo systemctl start sms-handler.service +``` + diff --git a/README.md b/README.md index cac3f40..2e3905d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,16 @@ +# About sms-handler + +[sms-handler](https://philo.ydns.eu) is a simple companion service of the +[SMS Forward](https://gitlab.com/pierreduchemin/smsforward) app +(available on [FDroid](https://f-droid.org/packages/com.pierreduchemin.smsforward/)) +designed to send notification mails on reception of SMSes. + +It is written in Python with [FastAPI](https://fastapi.tiangolo.com/) and BSD licensed. + +For a more flexible way to use *SMS Forward*, it can be used with +the [ntfy](https://ntfy.sh/) pub/sub service, making it easy +to integrate with other tools, for notification on various devices and automation. + ## Installation ```sh @@ -23,13 +36,14 @@ Configuration is done by environment variables, prefixed by `SMS_HANDLER_`: * `SMS_HANDLER_MAIL_TEMLATE`: used to format the body of the mails * `SMS_HANDLER_MAIL_ENABLE`: no or false to disable mails (default: yes) -The default settings assume that a mail server runs on the localhost, +The default settings work with a mail server runs on the localhost, and mails are sent to the user that owns the process. ### SMS handler app configuration Set the *Webhook URL* with as `https://your.server.name:8025/sms-handle`. -The URI path `/sms-handle` is not configurable. + +*Note*: the URI path `/sms-handle` is not configurable. ## Run @@ -45,11 +59,11 @@ Set port and listen address: sms-handler --port 80125 --host 192.168.100.55 ``` -### As a deamon (Systemd) +### As a daemon (Systemd) Create a service as follows. Pass the settings for the web server (listen address, port) in the ExecStart statement, -and the parameters as `Environment=MAIL_SERVER_START_TLS=yes`. +and the parameters as `Environment=SMS_HANDLER_MAIL_SERVER_START_TLS=yes`. ```systemd [Unit] @@ -65,92 +79,17 @@ Restart=on-failure WantedBy=default.target ``` -### Container +## Run behind a reverse proxy -Use `podman` or `docker`: +*sms-handler* can run behind a reverse proxy (Nginx, Apache, etc) +for HTTPs support. The URI (configured in the *SMS Forward* app) can be prefixed +to dispatch different applications on the same server. -```sh -podman run tiptop:5000/sms_handler -``` - -**Note**: the container is not published publicly yet! - -Pass parameters like this: - -```sh -podman run -e SMS_HANDLER_MAIL_SERVER_PORT=8025 -e SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com SMS_HANDLER_MAIL_TO=snoopy@peanuts.com tiptop:5000/sms_handler -``` - -#### Container as a systemd service - -Systemd can "orchestrate" containers easily thanks to podman. - -##### Rootlass (as regular user) - -For a rootless container, create a file in `.config/containers/systemd/sms-handler.container`: - -```systemd -[Container] -ContainerName=sms-handler -Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com -Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com -Image=tiptop:5000/sms_handler -Network=podman -PublishPort=8025:8025 - -[Service] -Restart=always - -[Install] -WantedBy=default.target -``` - -Request systemd to reload configuration, creating the service for this container: - -```sh -systemctl --user daemon-reload -``` - -Start the container as a service: - -```sh -systemctl start sms-handler.service -``` - -To start rootless containers at boot time, without the need for the user (snoopy) to log in: - -```sh -sudo loginctl enable-linger snoopy -``` - -##### Rootful (as root) - -For a rootless container, create a file in `/etc/containers/systemd/sms-handler.container`: - -```systemd -[Container] -ContainerName=sms-handler -Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com -Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com -Image=tiptop:5000/sms_handler -Network=podman -PublishPort=8025:8025 - -[Service] -Restart=always - -[Install] -WantedBy=default.target -``` - -Request systemd to reload configuration, creating the service for this container: - -```sh -sudo systemctl daemon-reload -``` - -Start the container as a service: - -```sh -sudo systemctl start sms-handler.service +Example for [Caddy](https://caddyserver.com/): + +```caddyfile +handle /sms-handler/* { + uri strip_prefix /sms-handler + reverse_proxy 192.168.100.2:8025 +} ``` From a0a6cd86b2022563aa2fd62e7b0220787509bff0 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 13:19:42 +0100 Subject: [PATCH 20/55] Update README --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 2e3905d..8dacbe6 100644 --- a/README.md +++ b/README.md @@ -93,3 +93,18 @@ handle /sms-handler/* { reverse_proxy 192.168.100.2:8025 } ``` + +## Development and test + +*sms-handler* is packaged with [uv](https://docs.astral.sh/uv/). + +Download source code, create a Python virtual environment, install dependencies and run the tests: + +```sh +git clone https://philo.ydns.eu/code/Philome/sms-handler.git +cd sms-handler +uv venv +uv sync +. .venv/bin/activate +pytest -s tests/basic.py +``` From ada878479145ba619b1ded391c4c9f48e5310792 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 13:20:47 +0100 Subject: [PATCH 21/55] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8dacbe6..a578b0f 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ designed to send notification mails on reception of SMSes. It is written in Python with [FastAPI](https://fastapi.tiangolo.com/) and BSD licensed. -For a more flexible way to use *SMS Forward*, it can be used with -the [ntfy](https://ntfy.sh/) pub/sub service, making it easy +For a more flexible way to use *SMS Forward*, use +the [ntfy](https://ntfy.sh/) pub/sub service. It makes it easy to integrate with other tools, for notification on various devices and automation. ## Installation From d89dddd4b53d7f67e9bb11c8cdbca75263bfa4e9 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 13:21:57 +0100 Subject: [PATCH 22/55] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a578b0f..ce01ddd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # About sms-handler -[sms-handler](https://philo.ydns.eu) is a simple companion service of the +[sms-handler](https://philo.ydns.eu/code/Philome/sms-handler) is a simple companion service of the [SMS Forward](https://gitlab.com/pierreduchemin/smsforward) app (available on [FDroid](https://f-droid.org/packages/com.pierreduchemin.smsforward/)) designed to send notification mails on reception of SMSes. From 6764edd6eed29b26328fcc25a4b27927eb9d4de7 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 13:24:04 +0100 Subject: [PATCH 23/55] Update README --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ce01ddd..6cefee2 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ # About sms-handler [sms-handler](https://philo.ydns.eu/code/Philome/sms-handler) is a simple companion service of the -[SMS Forward](https://gitlab.com/pierreduchemin/smsforward) app -(available on [FDroid](https://f-droid.org/packages/com.pierreduchemin.smsforward/)) -designed to send notification mails on reception of SMSes. +*SMS ForwardX* app to send notification mails on reception of SMSes. It is written in Python with [FastAPI](https://fastapi.tiangolo.com/) and BSD licensed. -For a more flexible way to use *SMS Forward*, use -the [ntfy](https://ntfy.sh/) pub/sub service. It makes it easy +For a more flexible way to use *SMS Forward* +(available on [FDroid](https://f-droid.org/packages/com.pierreduchemin.smsforward/)), +use the [ntfy](https://ntfy.sh/) pub/sub service. It makes it easy to integrate with other tools, for notification on various devices and automation. ## Installation From fa5c526bfaff956f6e38fd88b299a1c86a571297 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 13:24:24 +0100 Subject: [PATCH 24/55] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6cefee2..2f213db 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # About sms-handler [sms-handler](https://philo.ydns.eu/code/Philome/sms-handler) is a simple companion service of the -*SMS ForwardX* app to send notification mails on reception of SMSes. +*SMS Forward* app to send notification mails on reception of SMSes. It is written in Python with [FastAPI](https://fastapi.tiangolo.com/) and BSD licensed. From 8bf01b946fe269c46e1fc4441f47637b7c4d36c1 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 13:25:46 +0100 Subject: [PATCH 25/55] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f213db..1da4a64 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ to integrate with other tools, for notification on various devices and automatio PIP_EXTRA_INDEX_URL=https://pypi.org/simple/ pip install --index-url https://philo.ydns.eu/code/api/packages/Philome/pypi/simple/ sms-handler ``` -Debian and other systems require using `pipx` `pip`: +Debian and other systems prefer using `pipx` instead of `pip`: ```sh sudo apt install -y pipx From 85a97e9d9abf2537f623a0bfcd840f686a0424e7 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 1 Dec 2024 13:32:42 +0100 Subject: [PATCH 26/55] Update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1da4a64..67e1f11 100644 --- a/README.md +++ b/README.md @@ -107,3 +107,5 @@ uv sync . .venv/bin/activate pytest -s tests/basic.py ``` + +The *sms-handler* source has a CI test workflow for [Forgejo](https://forgejo.org/). From eae9b2771e99981ce25fe678bd578d6f6f396077 Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 2 Dec 2024 02:03:30 +0000 Subject: [PATCH 27/55] Update README.md --- README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 67e1f11..c14b0a6 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,6 @@ [sms-handler](https://philo.ydns.eu/code/Philome/sms-handler) is a simple companion service of the *SMS Forward* app to send notification mails on reception of SMSes. -It is written in Python with [FastAPI](https://fastapi.tiangolo.com/) and BSD licensed. - -For a more flexible way to use *SMS Forward* -(available on [FDroid](https://f-droid.org/packages/com.pierreduchemin.smsforward/)), -use the [ntfy](https://ntfy.sh/) pub/sub service. It makes it easy -to integrate with other tools, for notification on various devices and automation. ## Installation @@ -109,3 +103,16 @@ pytest -s tests/basic.py ``` The *sms-handler* source has a CI test workflow for [Forgejo](https://forgejo.org/). + +## Alternatives + +For a more flexible way to use *SMS Forward* +(available on [FDroid](https://f-droid.org/packages/com.pierreduchemin.smsforward/)), +use the [ntfy](https://ntfy.sh/) pub/sub service. It makes it easy +to integrate with other tools, for notification on various devices and automation. + +## License, etc + +*sms-handler* is BSD licensed. Suggestions and contributions are welcome. + +Donate using Liberapay \ No newline at end of file From e4422ca0fc93b7fd12338f152ab53ac60b898887 Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 2 Dec 2024 02:06:19 +0000 Subject: [PATCH 28/55] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c14b0a6..a4b2f03 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # About sms-handler [sms-handler](https://philo.ydns.eu/code/Philome/sms-handler) is a simple companion service of the -*SMS Forward* app to send notification mails on reception of SMSes. +[SMS Forward](https://gitlab.com/pierreduchemin/smsforward) app to send notification mails on reception of SMSes. ## Installation From 258ffb830f77137b9275be2e4cccf83342c8991c Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 2 Dec 2024 16:43:43 +0000 Subject: [PATCH 29/55] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a4b2f03..e140508 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ [sms-handler](https://philo.ydns.eu/code/Philome/sms-handler) is a simple companion service of the [SMS Forward](https://gitlab.com/pierreduchemin/smsforward) app to send notification mails on reception of SMSes. +Its main audience is for people who have a server on the public internet, configured with an email server +or configured with a mail relay. + ## Installation From f984f1e41e1a66cde3e79edaad1def29a088f9db Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 2 Dec 2024 16:45:12 +0000 Subject: [PATCH 30/55] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e140508..b9263e2 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ [sms-handler](https://philo.ydns.eu/code/Philome/sms-handler) is a simple companion service of the [SMS Forward](https://gitlab.com/pierreduchemin/smsforward) app to send notification mails on reception of SMSes. -Its main audience is for people who have a server on the public internet, configured with an email server -or configured with a mail relay. +Main audience: people who run a server on the public internet, configured with an email server +or configured as a relay. ## Installation From 41bef3b7b653ab12c7a187fd9ece8bf6edadf9ad Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 2 Dec 2024 16:55:51 +0000 Subject: [PATCH 31/55] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b9263e2..d9563e6 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,10 @@ sms-handler --port 80125 --host 192.168.100.55 ### As a daemon (Systemd) -Create a service as follows. -Pass the settings for the web server (listen address, port) in the ExecStart statement, -and the parameters as `Environment=SMS_HANDLER_MAIL_SERVER_START_TLS=yes`. +Create a service as below, adapt the email addresses (`snoopy@peanuts.com` in this example). + +Settings for the web server (listen address and port) are given in the ExecStart statement. +Other parameters are given as environment variables (see above). ```systemd [Unit] @@ -101,7 +102,7 @@ git clone https://philo.ydns.eu/code/Philome/sms-handler.git cd sms-handler uv venv uv sync -. .venv/bin/activate +. .venv/bin/activate # for bash. For fish: . .venv/bin/activate.fish pytest -s tests/basic.py ``` From c3ffd78b6bd32e1256ecca596760a8df161dad5a Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 2 Dec 2024 18:08:30 +0100 Subject: [PATCH 32/55] CI: build and publish --- .forgejo/workflows/build.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index b83a00b..6768d2d 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -26,3 +26,11 @@ jobs: - name: Run tests (API call) run: .venv/bin/pytest -s tests/basic.py + + - name: Build wheel + run: uv build --wheel + + - name: Publish to home + env: + LOCAL_PYPI_TOKEN: ${{ LOCAL_PYPI_TOKEN }} + run: uv publish --publish-url https://philo.ydns.eu/code/api/packages/philome/pypi --token $LOCAL_PYPI_TOKEN From 59c46ac2b711d33ccb5d2a73ef2991de70a70bfc Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 2 Dec 2024 18:13:04 +0100 Subject: [PATCH 33/55] CI: fix secret token --- .forgejo/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 6768d2d..67c5f78 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -32,5 +32,5 @@ jobs: - name: Publish to home env: - LOCAL_PYPI_TOKEN: ${{ LOCAL_PYPI_TOKEN }} + LOCAL_PYPI_TOKEN: ${{ secrets.LOCAL_PYPI_TOKEN }} run: uv publish --publish-url https://philo.ydns.eu/code/api/packages/philome/pypi --token $LOCAL_PYPI_TOKEN From cb842e8a7a19280a4e9279944f2f09cf4901d4be Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 2 Dec 2024 18:23:42 +0100 Subject: [PATCH 34/55] Bump version --- pyproject.toml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7522915..a5c3804 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.0" +version = "0.0.1" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" @@ -25,7 +25,4 @@ packages = ["src/sms_handler"] [tool.uv] package = true -dev-dependencies = [ - "httpx>=0.28.0", - "pytest>=8.3.3", -] +dev-dependencies = ["httpx>=0.28.0", "pytest>=8.3.3"] From 6d8743e1832ad1848222d8f0afc0e9728cdf3bf0 Mon Sep 17 00:00:00 2001 From: phil Date: Mon, 2 Dec 2024 18:46:39 +0100 Subject: [PATCH 35/55] Add version CLI option --- pyproject.toml | 2 +- src/sms_handler/server.py | 22 ++++++++++++++++++++-- uv.lock | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a5c3804..4140298 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.1" +version = "0.0.2" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" diff --git a/src/sms_handler/server.py b/src/sms_handler/server.py index aeceefc..a344ab2 100755 --- a/src/sms_handler/server.py +++ b/src/sms_handler/server.py @@ -84,8 +84,26 @@ def main(): from argparse import ArgumentParser parser = ArgumentParser(description=__doc__) - parser.add_argument("-l", "--host", default="0.0.0.0") - parser.add_argument("-p", "--port", type=int, default=8025) + parser.add_argument( + "-l", "--host", type=str, default="0.0.0.0", help="Addess to listen to" + ) + parser.add_argument( + "-p", "--port", type=int, default=8025, help="Port to listen to" + ) + parser.add_argument( + "-v", "--version", action="store_true", help="Print version and exit" + ) args = parser.parse_args() + if args.version: + import sys + from importlib.metadata import version + + print(version("sms_handler")) + sys.exit(0) + run(app, host=args.host, port=args.port) + + +if __name__ == "__main__": + main() diff --git a/uv.lock b/uv.lock index b81d553..7a3ebcb 100644 --- a/uv.lock +++ b/uv.lock @@ -255,7 +255,7 @@ wheels = [ [[package]] name = "sms-handler" -version = "0.0.0" +version = "0.0.1" source = { editable = "." } dependencies = [ { name = "aiosmtplib" }, From f6d0916decdbbfd208474b9867fccf2bd6a9bef9 Mon Sep 17 00:00:00 2001 From: phil Date: Tue, 3 Dec 2024 18:32:59 +0100 Subject: [PATCH 36/55] Add support for fromName, update README --- README.md | 62 +++++++++++++++++++++++++++++---------- pyproject.toml | 2 +- src/sms_handler/server.py | 7 +++-- tests/basic.py | 17 +++++++++++ 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d9563e6..c7c033e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # About sms-handler [sms-handler](https://philo.ydns.eu/code/Philome/sms-handler) is a simple companion service of the -[SMS Forward](https://gitlab.com/pierreduchemin/smsforward) app to send notification mails on reception of SMSes. +*SMS to URL Forwarder* app available on [F-Droid](https://f-droid.org/en/packages/tech.bogomolov.incomingsmsgateway/), +to send notification mails on reception of SMSes. Main audience: people who run a server on the public internet, configured with an email server or configured as a relay. - ## Installation ```sh @@ -20,6 +20,12 @@ sudo apt install -y pipx PIP_EXTRA_INDEX_URL=https://pypi.org/simple/ pipx install --index-url https://philo.ydns.eu/code/api/packages/Philome/pypi/simple/ sms-handler ``` +*sms-handler* can be run with any unprivileged user. +However due to standard security schemes, opening a port in the range 1-1024 +is allowed to privileged users only (such as *root*). +Use `sudo` to install as root if *sms-handler* is to be configured +with a reserved port. + ## Configuration Configuration is done by environment variables, prefixed by `SMS_HANDLER_`: @@ -29,12 +35,26 @@ Configuration is done by environment variables, prefixed by `SMS_HANDLER_`: * `SMS_HANDLER_MAIL_SERVER`: address of the mail server (default: localhost) * `SMS_HANDLER_MAIL_SERVER_PORT`: port of the mail server (default: 25) * `SMS_HANDLER_MAIL_SERVER_START_TLS`: initiate mail server connection with TLS (default: no) -* `SMS_HANDLER_MAIL_TEMLATE`: used to format the body of the mails +* `SMS_HANDLER_MAIL_TEMPLATE`: used to format the body of the mails * `SMS_HANDLER_MAIL_ENABLE`: no or false to disable mails (default: yes) The default settings work with a mail server runs on the localhost, and mails are sent to the user that owns the process. +The `SMS_HANDLER_MAIL_TEMPLATE` variable is used with Python `str.format()`. +The field names are the same as the default JSON setting in the *SMS to URL Forwarder* app. + +The default setting formats emails as below: + +```text +{text} +--- +From: {from} {fromName} +Sent: {sentStamp:%c} +Received: {receivedStamp:%c} +Sim: {sim} +``` + ### SMS handler app configuration Set the *Webhook URL* with as `https://your.server.name:8025/sms-handle`. @@ -78,19 +98,24 @@ WantedBy=default.target ## Run behind a reverse proxy -*sms-handler* can run behind a reverse proxy (Nginx, Apache, etc) -for HTTPs support. The URI (configured in the *SMS Forward* app) can be prefixed +*sms-handler* can run behind a reverse proxy (Nginx, Apache, Lighttpd, HAProxy, Caddy, Traefik, etc), +eg. for HTTPs support. The URI (configured in the *SMS to URL Forwarder* app) can be prefixed to dispatch different applications on the same server. -Example for [Caddy](https://caddyserver.com/): +[Caddy](https://caddyserver.com/) is suggested as it is straightforward to set up, with this Caddyfile extract: -```caddyfile -handle /sms-handler/* { +```text +my.example.com { + handle /sms-handler/* { uri strip_prefix /sms-handler - reverse_proxy 192.168.100.2:8025 + reverse_proxy 127.0.0.1:8025 + } } ``` +Replace the public host name `my.example.com` and +the destination IP:port `127.0.0.1:8025` for your setup. + ## Development and test *sms-handler* is packaged with [uv](https://docs.astral.sh/uv/). @@ -106,17 +131,24 @@ uv sync pytest -s tests/basic.py ``` -The *sms-handler* source has a CI test workflow for [Forgejo](https://forgejo.org/). +The *sms-handler* source includes a CI test workflow for [Forgejo](https://forgejo.org/). + +## Compatibility + +*sms-handler* is also compatible with +[this fork of *SMS to URL Forwarder*](>https://github.com/scottmconway/android_income_sms_gateway_webhook) +which includes also the sender's name. ## Alternatives -For a more flexible way to use *SMS Forward* -(available on [FDroid](https://f-droid.org/packages/com.pierreduchemin.smsforward/)), -use the [ntfy](https://ntfy.sh/) pub/sub service. It makes it easy -to integrate with other tools, for notification on various devices and automation. +For a more flexible way to use a tool like *SMS to URL Forwarder*, +a pub/sub service like [ntfy](https://ntfy.sh/) should be considered. +It makes it easy to integrate with other components +for notification on various devices and automation. ## License, etc *sms-handler* is BSD licensed. Suggestions and contributions are welcome. -Donate using Liberapay \ No newline at end of file +Donate using Liberapay + diff --git a/pyproject.toml b/pyproject.toml index 4140298..afcf963 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.2" +version = "0.0.4" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" diff --git a/src/sms_handler/server.py b/src/sms_handler/server.py index a344ab2..484aaaf 100755 --- a/src/sms_handler/server.py +++ b/src/sms_handler/server.py @@ -31,7 +31,7 @@ class MailSettings(BaseSettings): """\ {text} --- - From: {from} + From: {from} {fromName} Sent: {sentStamp:%c} Received: {receivedStamp:%c} Sim: {sim} @@ -45,7 +45,8 @@ class Settings(BaseSettings): class SmsAlert(BaseModel): - from_: str = Field(alias="from") + from_: str = Field(alias="from") # "from" is reserved word in Python, use an alias + fromName: str = "" text: str sentStamp: datetime receivedStamp: datetime @@ -61,7 +62,7 @@ def send_mail(smsAlert: SmsAlert): msg["Subject"] = f"SMS from {smsAlert.from_}" msg["From"] = settings.mail.sender msg["To"] = settings.mail.to - fmt = dict(smsAlert) + fmt = dict(smsAlert) # Make a dict for sms fmt["from"] = fmt.pop("from_") msg.set_content(settings.mail.template.format_map(fmt)) return send( diff --git a/tests/basic.py b/tests/basic.py index 4bd861b..a8fb4af 100644 --- a/tests/basic.py +++ b/tests/basic.py @@ -19,3 +19,20 @@ def test_handle_sms(): ) assert response.status_code == 200 assert response.json() == "OK" + + +def test_handle_sms_with_fromName(): + with TestClient(app) as client: + response = client.post( + "/handle-sms", + json={ + "text": "Hello!", + "from": "12345678", + "fromName": "foobar", + "sentStamp": "1234", + "receivedStamp": "1235", + "sim": "2", + }, + ) + assert response.status_code == 200 + assert response.json() == "OK" From d669251515f15a74ad168dcb2695ee69b4882def Mon Sep 17 00:00:00 2001 From: phil Date: Tue, 3 Dec 2024 18:39:32 +0100 Subject: [PATCH 37/55] Update README --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index c7c033e..fe7fbfb 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,18 @@ is allowed to privileged users only (such as *root*). Use `sudo` to install as root if *sms-handler* is to be configured with a reserved port. +### Upgrade + +```sh +pip upgrade sms-handler +``` + +or: + +```sh +pipx upgrade sms-handler +``` + ## Configuration Configuration is done by environment variables, prefixed by `SMS_HANDLER_`: From 087a343ebcf57165f14389a1b15343d026a7e7c3 Mon Sep 17 00:00:00 2001 From: phil Date: Thu, 5 Dec 2024 05:41:57 +0100 Subject: [PATCH 38/55] Fix and doc container --- CONTAINER.md | 16 +++++++--------- Containerfile | 6 +++--- README.md | 8 +++++++- pyproject.toml | 2 +- uv.lock | 2 +- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 546c8d3..176dbdc 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -3,15 +3,13 @@ Use `podman` or `docker`: ```sh -podman run tiptop:5000/sms_handler +podman run registry.philo.ydns.eu/philo/sms_handler ``` -**Note**: the container is not published publicly yet! - -Pass parameters like this: +Parameters are given as environment variables, eg.: ```sh -podman run -e SMS_HANDLER_MAIL_SERVER_PORT=8025 -e SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com SMS_HANDLER_MAIL_TO=snoopy@peanuts.com tiptop:5000/sms_handler +podman run -e SMS_HANDLER_MAIL_SERVER_PORT=8025 -e SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com SMS_HANDLER_MAIL_TO=snoopy@peanuts.com registry.philo.ydns.eu/philo/sms_handler ``` ## Container as a systemd service @@ -20,14 +18,15 @@ Systemd can "orchestrate" containers easily thanks to podman. ### Rootless (as regular user) -For a rootless container, create a file in `.config/containers/systemd/sms-handler.container`: +For a rootless container, create a file in `.config/containers/systemd/sms-handler.container`. +The configuration is given as environment variables (email addresses `snoopy@peanuts.com` in this example): ```systemd [Container] ContainerName=sms-handler Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com -Image=tiptop:5000/sms_handler +Image=registry.philo.ydns.eu/philo/sms_handler Network=podman PublishPort=8025:8025 @@ -65,7 +64,7 @@ For a rootless container, create a file in `/etc/containers/systemd/sms-handler. ContainerName=sms-handler Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com -Image=tiptop:5000/sms_handler +Image=registry.philo.ydns.eu/philo/sms_handler Network=podman PublishPort=8025:8025 @@ -87,4 +86,3 @@ Start the container as a service: ```sh sudo systemctl start sms-handler.service ``` - diff --git a/Containerfile b/Containerfile index 27b8457..0d48dde 100644 --- a/Containerfile +++ b/Containerfile @@ -1,4 +1,4 @@ -# Build: podman build -t sms-handler -f Containerfile +# Build: podman build -t registry.philo.ydns.eu/philo/sms_handler -f Containerfile FROM docker.io/python:3.12-alpine COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/ @@ -9,6 +9,6 @@ ENV SMS_HANDLER_MAIL_SERVER=host.containers.internal COPY . /src WORKDIR /src -RUN uv sync --frozen --no-cache +RUN uv sync --frozen --no-cache && uv pip install --system . -CMD .venv/bin/fastapi run server.py --port ${PORT} --host ${LISTEN_ADDR} +CMD sms-handler --port ${PORT} --host ${LISTEN_ADDR} diff --git a/README.md b/README.md index fe7fbfb..d9428de 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,13 @@ Restart=on-failure WantedBy=default.target ``` +### Run in a container + +*Sms-handler* is also packaged as a container for running with *docker*, +*podman*, or *Kunernetes*. + +See `CONTAINER.md` for the details. + ## Run behind a reverse proxy *sms-handler* can run behind a reverse proxy (Nginx, Apache, Lighttpd, HAProxy, Caddy, Traefik, etc), @@ -163,4 +170,3 @@ for notification on various devices and automation. *sms-handler* is BSD licensed. Suggestions and contributions are welcome. Donate using Liberapay - diff --git a/pyproject.toml b/pyproject.toml index afcf963..a19d8ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.4" +version = "0.0.5" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" diff --git a/uv.lock b/uv.lock index 7a3ebcb..bc5d7cd 100644 --- a/uv.lock +++ b/uv.lock @@ -255,7 +255,7 @@ wheels = [ [[package]] name = "sms-handler" -version = "0.0.1" +version = "0.0.4" source = { editable = "." } dependencies = [ { name = "aiosmtplib" }, From 1b419164dbf2e9a0a028d5368a2fcf38dad16883 Mon Sep 17 00:00:00 2001 From: phil Date: Thu, 5 Dec 2024 05:45:08 +0100 Subject: [PATCH 39/55] Fix and doc container --- CONTAINER.md | 8 ++++---- Containerfile | 2 +- pyproject.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 176dbdc..5ee352d 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -3,13 +3,13 @@ Use `podman` or `docker`: ```sh -podman run registry.philo.ydns.eu/philo/sms_handler +podman run registry.philo.ydns.eu/philo/sms-handler ``` Parameters are given as environment variables, eg.: ```sh -podman run -e SMS_HANDLER_MAIL_SERVER_PORT=8025 -e SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com SMS_HANDLER_MAIL_TO=snoopy@peanuts.com registry.philo.ydns.eu/philo/sms_handler +podman run -e SMS_HANDLER_MAIL_SERVER_PORT=8025 -e SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com SMS_HANDLER_MAIL_TO=snoopy@peanuts.com registry.philo.ydns.eu/philo/sms-handler ``` ## Container as a systemd service @@ -26,7 +26,7 @@ The configuration is given as environment variables (email addresses `snoopy@pea ContainerName=sms-handler Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com -Image=registry.philo.ydns.eu/philo/sms_handler +Image=registry.philo.ydns.eu/philo/sms-handler Network=podman PublishPort=8025:8025 @@ -64,7 +64,7 @@ For a rootless container, create a file in `/etc/containers/systemd/sms-handler. ContainerName=sms-handler Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com -Image=registry.philo.ydns.eu/philo/sms_handler +Image=registry.philo.ydns.eu/philo/sms-handler Network=podman PublishPort=8025:8025 diff --git a/Containerfile b/Containerfile index 0d48dde..88bf1c6 100644 --- a/Containerfile +++ b/Containerfile @@ -1,4 +1,4 @@ -# Build: podman build -t registry.philo.ydns.eu/philo/sms_handler -f Containerfile +# Build: podman build -t registry.philo.ydns.eu/philo/sms-handler -f Containerfile FROM docker.io/python:3.12-alpine COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/ diff --git a/pyproject.toml b/pyproject.toml index a19d8ae..0d4cd7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.5" +version = "0.0.6" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" From 12d071075d0539f17f4eaf660f3b297c54ee84a9 Mon Sep 17 00:00:00 2001 From: phil Date: Thu, 5 Dec 2024 14:59:03 +0100 Subject: [PATCH 40/55] Update base url for doc and container --- CONTAINER.md | 8 ++++---- Containerfile | 2 +- README.md | 8 ++++---- pyproject.toml | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 5ee352d..279d65e 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -3,13 +3,13 @@ Use `podman` or `docker`: ```sh -podman run registry.philo.ydns.eu/philo/sms-handler +podman run code.philo.ydns.eu/philorg/sms-handler ``` Parameters are given as environment variables, eg.: ```sh -podman run -e SMS_HANDLER_MAIL_SERVER_PORT=8025 -e SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com SMS_HANDLER_MAIL_TO=snoopy@peanuts.com registry.philo.ydns.eu/philo/sms-handler +podman run -e SMS_HANDLER_MAIL_SERVER_PORT=8025 -e SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com SMS_HANDLER_MAIL_TO=snoopy@peanuts.com code.philo.ydns.eu/philorg/sms-handler ``` ## Container as a systemd service @@ -26,7 +26,7 @@ The configuration is given as environment variables (email addresses `snoopy@pea ContainerName=sms-handler Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com -Image=registry.philo.ydns.eu/philo/sms-handler +Image=code.philo.ydns.eu/philorg/sms-handler Network=podman PublishPort=8025:8025 @@ -64,7 +64,7 @@ For a rootless container, create a file in `/etc/containers/systemd/sms-handler. ContainerName=sms-handler Environment=SMS_HANDLER_MAIL_SENDER=snoopy@peanuts.com Environment=SMS_HANDLER_MAIL_TO=snoopy@peanuts.com -Image=registry.philo.ydns.eu/philo/sms-handler +Image=code.philo.ydns.eu/philorg/sms-handler Network=podman PublishPort=8025:8025 diff --git a/Containerfile b/Containerfile index 88bf1c6..d413c9b 100644 --- a/Containerfile +++ b/Containerfile @@ -1,4 +1,4 @@ -# Build: podman build -t registry.philo.ydns.eu/philo/sms-handler -f Containerfile +# Build: podman build -t code.philo.ydns.eu/philorg/sms-handler -f Containerfile FROM docker.io/python:3.12-alpine COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/ diff --git a/README.md b/README.md index d9428de..61a39af 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # About sms-handler -[sms-handler](https://philo.ydns.eu/code/Philome/sms-handler) is a simple companion service of the +[sms-handler](https://code.philo.ydns.eu/philorg/sms-handler) is a simple companion service of the *SMS to URL Forwarder* app available on [F-Droid](https://f-droid.org/en/packages/tech.bogomolov.incomingsmsgateway/), to send notification mails on reception of SMSes. @@ -10,14 +10,14 @@ or configured as a relay. ## Installation ```sh -PIP_EXTRA_INDEX_URL=https://pypi.org/simple/ pip install --index-url https://philo.ydns.eu/code/api/packages/Philome/pypi/simple/ sms-handler +PIP_EXTRA_INDEX_URL=https://pypi.org/simple/ pip install --index-url https://code.philo.ydns.eu/api/packages/philorg/pypi/simple/ sms-handler ``` Debian and other systems prefer using `pipx` instead of `pip`: ```sh sudo apt install -y pipx -PIP_EXTRA_INDEX_URL=https://pypi.org/simple/ pipx install --index-url https://philo.ydns.eu/code/api/packages/Philome/pypi/simple/ sms-handler +PIP_EXTRA_INDEX_URL=https://pypi.org/simple/ pipx install --index-url https://code.philo.ydns.eu/api/packages/philorg/pypi/simple/ sms-handler ``` *sms-handler* can be run with any unprivileged user. @@ -142,7 +142,7 @@ the destination IP:port `127.0.0.1:8025` for your setup. Download source code, create a Python virtual environment, install dependencies and run the tests: ```sh -git clone https://philo.ydns.eu/code/Philome/sms-handler.git +git clone https://code.philo.ydns.eu/philorg/sms-handler.git cd sms-handler uv venv uv sync diff --git a/pyproject.toml b/pyproject.toml index 0d4cd7d..5d99793 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.6" +version = "0.0.7" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" From 27fd5074f2f38ca85efbdc6e556781d06d460682 Mon Sep 17 00:00:00 2001 From: phil Date: Thu, 5 Dec 2024 18:30:31 +0100 Subject: [PATCH 41/55] Update base url for CI --- .forgejo/workflows/build.yaml | 2 +- pyproject.toml | 2 +- uv.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 67c5f78..11bb005 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -33,4 +33,4 @@ jobs: - name: Publish to home env: LOCAL_PYPI_TOKEN: ${{ secrets.LOCAL_PYPI_TOKEN }} - run: uv publish --publish-url https://philo.ydns.eu/code/api/packages/philome/pypi --token $LOCAL_PYPI_TOKEN + run: uv publish --publish-url https://code.philo.ydns.eu/api/packages/philome/pypi --token $LOCAL_PYPI_TOKEN diff --git a/pyproject.toml b/pyproject.toml index 5d99793..d7c6188 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.7" +version = "0.0.8" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" diff --git a/uv.lock b/uv.lock index bc5d7cd..484bbc3 100644 --- a/uv.lock +++ b/uv.lock @@ -255,7 +255,7 @@ wheels = [ [[package]] name = "sms-handler" -version = "0.0.4" +version = "0.0.8" source = { editable = "." } dependencies = [ { name = "aiosmtplib" }, From 3cad00d336c1ce81b408bc9e8f8aa40a5645a59d Mon Sep 17 00:00:00 2001 From: phil Date: Thu, 5 Dec 2024 18:37:07 +0100 Subject: [PATCH 42/55] Update base url for CI --- .forgejo/workflows/build.yaml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 11bb005..7409e2f 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -33,4 +33,4 @@ jobs: - name: Publish to home env: LOCAL_PYPI_TOKEN: ${{ secrets.LOCAL_PYPI_TOKEN }} - run: uv publish --publish-url https://code.philo.ydns.eu/api/packages/philome/pypi --token $LOCAL_PYPI_TOKEN + run: uv publish --publish-url https://code.philo.ydns.eu/api/packages/philorg/pypi --token $LOCAL_PYPI_TOKEN diff --git a/pyproject.toml b/pyproject.toml index d7c6188..69149da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.8" +version = "0.0.9" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" From b75e2e9971e1e597ee9d31a21ef23fe89d8a053f Mon Sep 17 00:00:00 2001 From: phil Date: Fri, 6 Dec 2024 01:47:03 +0100 Subject: [PATCH 43/55] CI: build container --- .forgejo/workflows/build.yaml | 36 ++++++++++++++++++++++++++++++++++- pyproject.toml | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 7409e2f..ac10775 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -30,7 +30,41 @@ jobs: - name: Build wheel run: uv build --wheel - - name: Publish to home + - name: Publish Python package (home) env: LOCAL_PYPI_TOKEN: ${{ secrets.LOCAL_PYPI_TOKEN }} run: uv publish --publish-url https://code.philo.ydns.eu/api/packages/philorg/pypi --token $LOCAL_PYPI_TOKEN + + - name: Get version + id: version + run: echo "version=$(.venv/bin/sms-handler --version)" >> $GITHUB_OUTPUT + + - name: Workaround for bug of podman-login + run: | + mkdir -p $HOME/.docker + echo "{ \"auths\": {} }" > $HOME/.docker/config.json + + - name: Log in to the container registry (with another workaround) + uses: actions/podman-login@v1 + with: + registry: ${{ vars.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + auth_file_path: /tmp/auth.json + + - name: Build the container image + uses: actions/buildah-build@v1 + with: + image: sms-handler + oci: true + labels: sms-handler + tags: ${{ steps.version.outputs.version }} + containerfiles: | + ./Containerfile + + - name: Push the image to the registry + uses: actions/push-to-registry@v2 + with: + registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}" + image: ssm-handler + tags: ${{ steps.version.outputs.version }} diff --git a/pyproject.toml b/pyproject.toml index 69149da..3478423 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.9" +version = "0.0.10" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" From 719ba3b1eac72c7cf8dde8139cad58499b9f0c70 Mon Sep 17 00:00:00 2001 From: phil Date: Fri, 6 Dec 2024 02:06:07 +0100 Subject: [PATCH 44/55] CI: build container --- .forgejo/workflows/build.yaml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index ac10775..021698e 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -66,5 +66,5 @@ jobs: uses: actions/push-to-registry@v2 with: registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}" - image: ssm-handler + image: sms-handler tags: ${{ steps.version.outputs.version }} diff --git a/pyproject.toml b/pyproject.toml index 3478423..9bed3df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.10" +version = "0.0.11" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" From 60db7970515fe1a68f6ab81fc300f77d461a4157 Mon Sep 17 00:00:00 2001 From: phil Date: Fri, 6 Dec 2024 20:02:48 +0100 Subject: [PATCH 45/55] CI: publish container with tag latest along with the version --- .forgejo/workflows/build.yaml | 9 ++++++++- pyproject.toml | 2 +- uv.lock | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 021698e..e006572 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -62,9 +62,16 @@ jobs: containerfiles: | ./Containerfile - - name: Push the image to the registry + - name: Push the image to the registry (version tagged) uses: actions/push-to-registry@v2 with: registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}" image: sms-handler tags: ${{ steps.version.outputs.version }} + + - name: Push the image to the registry (latest tag) + uses: actions/push-to-registry@v2 + with: + registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}" + image: sms-handler + tags: latest diff --git a/pyproject.toml b/pyproject.toml index 9bed3df..0226ea8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.11" +version = "0.0.12" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" diff --git a/uv.lock b/uv.lock index 484bbc3..0009e95 100644 --- a/uv.lock +++ b/uv.lock @@ -255,7 +255,7 @@ wheels = [ [[package]] name = "sms-handler" -version = "0.0.8" +version = "0.0.12" source = { editable = "." } dependencies = [ { name = "aiosmtplib" }, From d159254fdf83b2e18fa78cc3fb6d4390ef430c2f Mon Sep 17 00:00:00 2001 From: phil Date: Fri, 6 Dec 2024 20:19:36 +0100 Subject: [PATCH 46/55] CI: build container with version and latest tags --- .forgejo/workflows/build.yaml | 13 +++---------- pyproject.toml | 2 +- uv.lock | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index e006572..67944bf 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -58,20 +58,13 @@ jobs: image: sms-handler oci: true labels: sms-handler - tags: ${{ steps.version.outputs.version }} + tags: latest ${{ steps.version.outputs.version }} containerfiles: | ./Containerfile - - name: Push the image to the registry (version tagged) + - name: Push the image to the registry uses: actions/push-to-registry@v2 with: registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}" image: sms-handler - tags: ${{ steps.version.outputs.version }} - - - name: Push the image to the registry (latest tag) - uses: actions/push-to-registry@v2 - with: - registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}" - image: sms-handler - tags: latest + tags: latest ${{ steps.version.outputs.version }} diff --git a/pyproject.toml b/pyproject.toml index 0226ea8..31e174a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.12" +version = "0.0.13" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" diff --git a/uv.lock b/uv.lock index 0009e95..39e1c0b 100644 --- a/uv.lock +++ b/uv.lock @@ -255,7 +255,7 @@ wheels = [ [[package]] name = "sms-handler" -version = "0.0.12" +version = "0.0.13" source = { editable = "." } dependencies = [ { name = "aiosmtplib" }, From ab824960acd03b5dbf5754a8c503e47b077f8198 Mon Sep 17 00:00:00 2001 From: phil Date: Fri, 13 Dec 2024 15:57:40 +0100 Subject: [PATCH 47/55] Add suject template, pretty_from field and more mail server configuration --- README.md | 17 +++- pyproject.toml | 9 +- src/sms_handler/server.py | 68 +++++++------- uv.lock | 181 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 237 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 61a39af..06eb393 100644 --- a/README.md +++ b/README.md @@ -46,22 +46,33 @@ Configuration is done by environment variables, prefixed by `SMS_HANDLER_`: * `SMS_HANDLER_MAIL_TO`: destination email address (default: *username*@localhost) * `SMS_HANDLER_MAIL_SERVER`: address of the mail server (default: localhost) * `SMS_HANDLER_MAIL_SERVER_PORT`: port of the mail server (default: 25) -* `SMS_HANDLER_MAIL_SERVER_START_TLS`: initiate mail server connection with TLS (default: no) +* `SMS_HANDLER_MAIL_SERVER_USERNAME`: username of the mail server (default: None) +* `SMS_HANDLER_MAIL_SERVER_PASSWORD`: port of the mail server (default: None) +* `SMS_HANDLER_MAIL_SERVER_START_TLS`: initiate mail server connection with TLS, eg. when using port 587 (default: no) +* `SMS_HANDLER_MAIL_SERVER_USE_TLS`: use mail server connection TLS, eg when using prot 465 (default: no) +* `SMS_HANDLER_MAIL_SUBJECT`: used to format the subject of the mails * `SMS_HANDLER_MAIL_TEMPLATE`: used to format the body of the mails * `SMS_HANDLER_MAIL_ENABLE`: no or false to disable mails (default: yes) The default settings work with a mail server runs on the localhost, and mails are sent to the user that owns the process. -The `SMS_HANDLER_MAIL_TEMPLATE` variable is used with Python `str.format()`. +### Templates + +The `SMS_HANDLER_MAIL_TEMPLATE` and `SMS_HANDLER_MAIL_SUBJECT` variable use the standard +Python [string formatting](https://peps.python.org/pep-3101/). +See for example [here](https://www.w3schools.com/python/python_string_formatting.asp) for simple examples. + The field names are the same as the default JSON setting in the *SMS to URL Forwarder* app. +Additionally, a `pretty_from` variable can be used in the templates, which combines the phone number +of the sender with its name if found in the SMS record in the `fromName` field. The default setting formats emails as below: ```text {text} --- -From: {from} {fromName} +From: {pretty_from} Sent: {sentStamp:%c} Received: {receivedStamp:%c} Sim: {sim} diff --git a/pyproject.toml b/pyproject.toml index 31e174a..0ec73e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.13" +version = "0.0.14" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" @@ -25,4 +25,9 @@ packages = ["src/sms_handler"] [tool.uv] package = true -dev-dependencies = ["httpx>=0.28.0", "pytest>=8.3.3"] +dev-dependencies = [ + "httpx>=0.28.0", + "ipdb>=0.13.13", + "ipython>=8.30.0", + "pytest>=8.3.3", +] diff --git a/src/sms_handler/server.py b/src/sms_handler/server.py index 484aaaf..1a064d3 100755 --- a/src/sms_handler/server.py +++ b/src/sms_handler/server.py @@ -11,7 +11,7 @@ from email.message import EmailMessage from fastapi import FastAPI from aiosmtplib import send -from pydantic import BaseModel, Field, EmailStr +from pydantic import BaseModel, Field, computed_field from pydantic_settings import BaseSettings, SettingsConfigDict USER = os.environ.get("USER", "root") @@ -24,14 +24,12 @@ class MailSettings(BaseSettings): enable: bool = True sender: str = f"{USER}@{HOST_NAME}" to: str = f"{USER}@{HOST_NAME}" - server: str = "localhost" - server_port: int = 25 - server_start_tls: bool = False + subject: str = "SMS from {pretty_from}" template: str = textwrap.dedent( """\ {text} --- - From: {from} {fromName} + From: {pretty_from} Sent: {sentStamp:%c} Received: {receivedStamp:%c} Sim: {sim} @@ -39,19 +37,38 @@ class MailSettings(BaseSettings): ) +class MailServerSettings(BaseSettings): + model_config = SettingsConfigDict(env_prefix="SMS_HANDLER_MAIL_SERVER_") + hostname: str = "localhost" + port: int = 25 + username: str | None = None + password: str | None = None + start_tls: bool = False + use_tls: bool = False + + class Settings(BaseSettings): model_config = SettingsConfigDict(env_prefix="SMS_HANDLER_") mail: MailSettings = MailSettings() + mail_server: MailServerSettings = MailServerSettings() class SmsAlert(BaseModel): - from_: str = Field(alias="from") # "from" is reserved word in Python, use an alias - fromName: str = "" + from_: str = Field(alias="from") + fromName: str | None = None text: str sentStamp: datetime receivedStamp: datetime sim: str + @computed_field + @property + def pretty_from(self) -> str: + if self.fromName is not None: + return f"{self.fromName} ({self.from_})" + else: + return self.from_ + settings = Settings() app = FastAPI() @@ -59,17 +76,20 @@ app = FastAPI() def send_mail(smsAlert: SmsAlert): msg = EmailMessage() - msg["Subject"] = f"SMS from {smsAlert.from_}" + fmt = smsAlert.model_dump() + fmt["from"] = fmt.pop("from_") + msg["Subject"] = settings.mail.subject.format_map(fmt) msg["From"] = settings.mail.sender msg["To"] = settings.mail.to - fmt = dict(smsAlert) # Make a dict for sms - fmt["from"] = fmt.pop("from_") msg.set_content(settings.mail.template.format_map(fmt)) return send( msg, - hostname=settings.mail.server, - port=settings.mail.server_port, - start_tls=settings.mail.server_start_tls, + hostname=settings.mail_server.hostname, + username=settings.mail_server.username, + password=settings.mail_server.password, + port=settings.mail_server.port, + start_tls=settings.mail_server.start_tls, + use_tls=settings.mail_server.use_tls, ) @@ -85,26 +105,8 @@ def main(): from argparse import ArgumentParser parser = ArgumentParser(description=__doc__) - parser.add_argument( - "-l", "--host", type=str, default="0.0.0.0", help="Addess to listen to" - ) - parser.add_argument( - "-p", "--port", type=int, default=8025, help="Port to listen to" - ) - parser.add_argument( - "-v", "--version", action="store_true", help="Print version and exit" - ) + parser.add_argument("-l", "--host", default="0.0.0.0") + parser.add_argument("-p", "--port", type=int, default=8025) args = parser.parse_args() - if args.version: - import sys - from importlib.metadata import version - - print(version("sms_handler")) - sys.exit(0) - run(app, host=args.host, port=args.port) - - -if __name__ == "__main__": - main() diff --git a/uv.lock b/uv.lock index 39e1c0b..3731075 100644 --- a/uv.lock +++ b/uv.lock @@ -32,6 +32,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 }, ] +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, +] + [[package]] name = "certifi" version = "2024.8.30" @@ -62,6 +71,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] +[[package]] +name = "decorator" +version = "5.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, +] + +[[package]] +name = "executing" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/7d45f492c2c4a0e8e0fad57d081a7c8a0286cdd86372b070cca1ec0caa1e/executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab", size = 977485 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/fd/afcd0496feca3276f509df3dbd5dae726fcc756f1a08d9e25abe1733f962/executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf", size = 25805 }, +] + [[package]] name = "fastapi" version = "0.115.5" @@ -131,6 +158,64 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, ] +[[package]] +name = "ipdb" +version = "0.13.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "decorator" }, + { name = "ipython" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/1b/7e07e7b752017f7693a0f4d41c13e5ca29ce8cbcfdcc1fd6c4ad8c0a27a0/ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726", size = 17042 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/4c/b075da0092003d9a55cf2ecc1cae9384a1ca4f650d51b00fc59875fe76f6/ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", size = 12130 }, +] + +[[package]] +name = "ipython" +version = "8.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/8b/710af065ab8ed05649afa5bd1e07401637c9ec9fb7cfda9eac7e91e9fbd4/ipython-8.30.0.tar.gz", hash = "sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e", size = 5592205 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/f3/1332ba2f682b07b304ad34cad2f003adcfeb349486103f4b632335074a7c/ipython-8.30.0-py3-none-any.whl", hash = "sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321", size = 820765 }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + [[package]] name = "packaging" version = "24.2" @@ -140,6 +225,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, ] +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -149,6 +255,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] +[[package]] +name = "prompt-toolkit" +version = "3.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/4f/feb5e137aff82f7c7f3248267b97451da3644f6cdc218edfe549fb354127/prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90", size = 424684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/6a/fd08d94654f7e67c52ca30523a178b3f8ccc4237fce4be90d39c938a831a/prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e", size = 386595 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + [[package]] name = "pydantic" version = "2.10.2" @@ -229,6 +365,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/f9/ff95fd7d760af42f647ea87f9b8a383d891cdb5e5dbd4613edaeb094252a/pydantic_settings-2.6.1-py3-none-any.whl", hash = "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87", size = 28595 }, ] +[[package]] +name = "pygments" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, +] + [[package]] name = "pytest" version = "8.3.3" @@ -267,6 +412,8 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "httpx" }, + { name = "ipdb" }, + { name = "ipython" }, { name = "pytest" }, ] @@ -281,6 +428,8 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "httpx", specifier = ">=0.28.0" }, + { name = "ipdb", specifier = ">=0.13.13" }, + { name = "ipython", specifier = ">=8.30.0" }, { name = "pytest", specifier = ">=8.3.3" }, ] @@ -293,6 +442,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, ] +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + [[package]] name = "starlette" version = "0.41.3" @@ -305,6 +468,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/00/2b325970b3060c7cecebab6d295afe763365822b1306a12eeab198f74323/starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7", size = 73225 }, ] +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + [[package]] name = "typing-extensions" version = "4.12.2" @@ -326,3 +498,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/6a/3c/21dba3e7d76138725 wheels = [ { url = "https://files.pythonhosted.org/packages/50/c1/2d27b0a15826c2b71dcf6e2f5402181ef85acf439617bb2f1453125ce1f3/uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e", size = 63828 }, ] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] From 9650ac90cb0dba944525ccfda3dfb007ff0c95ec Mon Sep 17 00:00:00 2001 From: phil Date: Tue, 17 Dec 2024 22:08:22 +0100 Subject: [PATCH 48/55] Added back version which got removed in the previous version --- src/sms_handler/server.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/sms_handler/server.py b/src/sms_handler/server.py index 1a064d3..db430e7 100755 --- a/src/sms_handler/server.py +++ b/src/sms_handler/server.py @@ -105,8 +105,22 @@ def main(): from argparse import ArgumentParser parser = ArgumentParser(description=__doc__) - parser.add_argument("-l", "--host", default="0.0.0.0") - parser.add_argument("-p", "--port", type=int, default=8025) + parser.add_argument( + "-l", "--host", type=str, default="0.0.0.0", help="Address to listen to" + ) + parser.add_argument( + "-p", "--port", type=int, default=8025, help="Port to listen to" + ) + parser.add_argument( + "-v", "--version", action="store_true", help="Print version and exit" + ) args = parser.parse_args() + if args.version: + import sys + from importlib.metadata import version + + print(version("sms_handler")) + sys.exit(0) + run(app, host=args.host, port=args.port) From a6bb7b0350027962625ae9d5cb4978ce1b759946 Mon Sep 17 00:00:00 2001 From: phil Date: Tue, 17 Dec 2024 22:08:28 +0100 Subject: [PATCH 49/55] Update README after review by Charles --- README.md | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 06eb393..2607658 100644 --- a/README.md +++ b/README.md @@ -80,29 +80,23 @@ Sim: {sim} ### SMS handler app configuration -Set the *Webhook URL* with as `https://your.server.name:8025/sms-handle`. +Set the *Webhook URL* to `http://your.server.name:8025/handle-sms`. -*Note*: the URI path `/sms-handle` is not configurable. +Adjust with the public name (`your.server.name`) and the port where the +service is exposed. This corresponds to the +`--port` option of `sms-handler`. See also the *Reverse proxy* note below. + +*Note*: the URI path `/handle-sms` is not configurable. ## Run -### In foreground - -```sh -sms-handler -``` - -Set port and listen address: - -```sh -sms-handler --port 80125 --host 192.168.100.55 -``` - ### As a daemon (Systemd) Create a service as below, adapt the email addresses (`snoopy@peanuts.com` in this example). -Settings for the web server (listen address and port) are given in the ExecStart statement. +Settings for the web server (listen address and port) can be given +in the ExecStart statement if the default values need to be adapted. + Other parameters are given as environment variables (see above). ```systemd @@ -146,6 +140,22 @@ my.example.com { Replace the public host name `my.example.com` and the destination IP:port `127.0.0.1:8025` for your setup. +### In foreground + +A service like `sms-handler` is designed to run as a background service (daemon).the + +However it is possible to run it manually: + +```sh +sms-handler +``` + +Set port and listen address: + +```sh +sms-handler --port 80125 --host 192.168.100.55 +``` + ## Development and test *sms-handler* is packaged with [uv](https://docs.astral.sh/uv/). @@ -166,7 +176,7 @@ The *sms-handler* source includes a CI test workflow for [Forgejo](https://forge ## Compatibility *sms-handler* is also compatible with -[this fork of *SMS to URL Forwarder*](>https://github.com/scottmconway/android_income_sms_gateway_webhook) +[this fork of *SMS to URL Forwarder*](https://github.com/scottmconway/android_income_sms_gateway_webhook) which includes also the sender's name. ## Alternatives From 368bc01caf780f3e74b9261d26e5d29a77ddc9f0 Mon Sep 17 00:00:00 2001 From: phil Date: Tue, 17 Dec 2024 22:12:24 +0100 Subject: [PATCH 50/55] Bump version --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0ec73e1..701e30b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.14" +version = "0.0.15" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" diff --git a/uv.lock b/uv.lock index 3731075..e662cd3 100644 --- a/uv.lock +++ b/uv.lock @@ -400,7 +400,7 @@ wheels = [ [[package]] name = "sms-handler" -version = "0.0.13" +version = "0.0.15" source = { editable = "." } dependencies = [ { name = "aiosmtplib" }, From 9e6d75ef4ad06321db9ccea26c427b8f45dcba05 Mon Sep 17 00:00:00 2001 From: phil Date: Tue, 17 Dec 2024 21:29:22 +0000 Subject: [PATCH 51/55] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2607658..5ee5dc8 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,13 @@ with a reserved port. ### Upgrade ```sh -pip upgrade sms-handler +pip upgrade --index-url https://code.philo.ydns.eu/api/packages/philorg/pypi/simple sms-handler ``` or: ```sh -pipx upgrade sms-handler +pipx upgrade --index-url https://code.philo.ydns.eu/api/packages/philorg/pypi/simple sms-handler ``` ## Configuration From 26886695673adb028d88ca6d7b5506c18b392aff Mon Sep 17 00:00:00 2001 From: phil Date: Tue, 17 Dec 2024 22:30:48 +0100 Subject: [PATCH 52/55] Bump version --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 701e30b..565bc52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.15" +version = "0.0.16" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md" diff --git a/uv.lock b/uv.lock index e662cd3..bfe063d 100644 --- a/uv.lock +++ b/uv.lock @@ -400,7 +400,7 @@ wheels = [ [[package]] name = "sms-handler" -version = "0.0.15" +version = "0.0.16" source = { editable = "." } dependencies = [ { name = "aiosmtplib" }, From 848460955b45d18e1d5bb0ce6bdc5d473c54bb19 Mon Sep 17 00:00:00 2001 From: phil Date: Sat, 21 Dec 2024 17:28:06 +0000 Subject: [PATCH 53/55] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ee5dc8..a87d18d 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ the destination IP:port `127.0.0.1:8025` for your setup. ### In foreground -A service like `sms-handler` is designed to run as a background service (daemon).the +A service like `sms-handler` is designed to run as a background service (daemon). However it is possible to run it manually: @@ -150,6 +150,9 @@ However it is possible to run it manually: sms-handler ``` +Note that the default port is 8025, as it is not a restricted port so does not +need privileged permissions, aka root/sudo. + Set port and listen address: ```sh From cd76a8408d89358c8c07baacceb4a71db77cf6f9 Mon Sep 17 00:00:00 2001 From: phil Date: Sat, 21 Dec 2024 18:36:17 +0100 Subject: [PATCH 54/55] Added default values for cli --help option --- src/sms_handler/server.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sms_handler/server.py b/src/sms_handler/server.py index db430e7..c9d1a0b 100755 --- a/src/sms_handler/server.py +++ b/src/sms_handler/server.py @@ -24,7 +24,7 @@ class MailSettings(BaseSettings): enable: bool = True sender: str = f"{USER}@{HOST_NAME}" to: str = f"{USER}@{HOST_NAME}" - subject: str = "SMS from {pretty_from}" + subject: str = """SMS from {pretty_from}""" template: str = textwrap.dedent( """\ {text} @@ -106,10 +106,14 @@ def main(): parser = ArgumentParser(description=__doc__) parser.add_argument( - "-l", "--host", type=str, default="0.0.0.0", help="Address to listen to" + "-l", + "--host", + type=str, + default="0.0.0.0", + help="Address to listen to (default: 0.0.0.0)", ) parser.add_argument( - "-p", "--port", type=int, default=8025, help="Port to listen to" + "-p", "--port", type=int, default=8025, help="Port to listen to (default: 8025)" ) parser.add_argument( "-v", "--version", action="store_true", help="Print version and exit" From 6ad8160155195424225f6e873bae3891cd5b7fd6 Mon Sep 17 00:00:00 2001 From: phil Date: Sat, 21 Dec 2024 18:36:46 +0100 Subject: [PATCH 55/55] Bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 565bc52..bb13f74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sms_handler" -version = "0.0.16" +version = "0.0.17" #dynamic = ["version"] description = "Listen to messages from the SMS Forwarder app on Android and send mail" readme = "README.md"