amoorion 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. amoorion-0.3.0/LICENSE +21 -0
  2. amoorion-0.3.0/PKG-INFO +122 -0
  3. amoorion-0.3.0/README.md +102 -0
  4. amoorion-0.3.0/pyproject.toml +127 -0
  5. amoorion-0.3.0/pyproject.toml.orig +111 -0
  6. amoorion-0.3.0/src/amoorion/__init__.py +12 -0
  7. amoorion-0.3.0/src/amoorion/api/__init__.py +5 -0
  8. amoorion-0.3.0/src/amoorion/api/__main__.py +5 -0
  9. amoorion-0.3.0/src/amoorion/api/base.py +87 -0
  10. amoorion-0.3.0/src/amoorion/cli/__init__.py +5 -0
  11. amoorion-0.3.0/src/amoorion/cli/__main__.py +5 -0
  12. amoorion-0.3.0/src/amoorion/cli/base.py +140 -0
  13. amoorion-0.3.0/src/amoorion/cli/guide.py +141 -0
  14. amoorion-0.3.0/src/amoorion/core.py +86 -0
  15. amoorion-0.3.0/src/amoorion/daemon/__init__.py +5 -0
  16. amoorion-0.3.0/src/amoorion/daemon/__main__.py +5 -0
  17. amoorion-0.3.0/src/amoorion/daemon/base.py +62 -0
  18. amoorion-0.3.0/src/amoorion/hooks/__init__.py +5 -0
  19. amoorion-0.3.0/src/amoorion/hooks/base.py +72 -0
  20. amoorion-0.3.0/src/amoorion/inbox/__init__.py +5 -0
  21. amoorion-0.3.0/src/amoorion/inbox/base.py +81 -0
  22. amoorion-0.3.0/src/amoorion/mcp/__init__.py +5 -0
  23. amoorion-0.3.0/src/amoorion/mcp/__main__.py +5 -0
  24. amoorion-0.3.0/src/amoorion/mcp/base.py +62 -0
  25. amoorion-0.3.0/src/amoorion/methods/__init__.py +27 -0
  26. amoorion-0.3.0/src/amoorion/methods/base.py +175 -0
  27. amoorion-0.3.0/src/amoorion/models/__init__.py +5 -0
  28. amoorion-0.3.0/src/amoorion/models/base.py +45 -0
  29. amoorion-0.3.0/src/amoorion/plugins/__init__.py +16 -0
  30. amoorion-0.3.0/src/amoorion/plugins/base.py +83 -0
  31. amoorion-0.3.0/src/amoorion/plugins/dryrun/__init__.py +5 -0
  32. amoorion-0.3.0/src/amoorion/plugins/dryrun/base.py +38 -0
  33. amoorion-0.3.0/src/amoorion/plugins/http.py +57 -0
  34. amoorion-0.3.0/src/amoorion/vault/__init__.py +9 -0
  35. amoorion-0.3.0/src/amoorion/vault/base.py +69 -0
  36. amoorion-0.3.0/src/amoorion/vault/bw_cli.py +90 -0
  37. amoorion-0.3.0/src/amoorion/vault/default.py +28 -0
  38. amoorion-0.3.0/src/amoorion/vault/http_vault.py +55 -0
  39. amoorion-0.3.0/src/amoorion/vault/kpxc_db_file.py +59 -0
amoorion-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Sett Sarverott
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: amoorion
3
+ Version: 0.3.0
4
+ Summary: Universal tool for administration of chatting and communication platforms
5
+ Author: Sett Sarverott
6
+ Author-email: Sett Sarverott <sett@sarverott.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Dist: fastapi>=0.162.0
10
+ Requires-Dist: mcp[cli]>=2.3.0
11
+ Requires-Dist: pydantic>=2.14.1
12
+ Requires-Dist: python-dotenv>=1.2.4
13
+ Requires-Dist: requests>=2.34.2
14
+ Requires-Dist: rich>=15.0.0
15
+ Requires-Dist: uvicorn>=0.54.0
16
+ Requires-Dist: pykeepass>=4.2.0 ; extra == 'keepassxc'
17
+ Requires-Python: >=3.12
18
+ Provides-Extra: keepassxc
19
+ Description-Content-Type: text/markdown
20
+
21
+ # amoorion
22
+ Universal tool for administration of chatting and communication platforms
23
+
24
+ ---
25
+
26
+ ## **EARLY STAGE OF PROJECT**
27
+
28
+ The core works end to end, with plugins for Bluesky, Mastodon, X, Tumblr,
29
+ Blogger, Facebook, Discord (bot or webhook), Telegram and Matrix, tested
30
+ only against fakes of their APIs so far, not yet against real accounts. `dryrun` sends nothing, for trying.
31
+ Plan: [docs/devlog/VISION.md](docs/devlog/VISION.md).
32
+
33
+ ---
34
+
35
+ ## What it will do
36
+
37
+ Write once, post everywhere, read and answer from one place, and link it
38
+ with the services around it.
39
+
40
+ #### Publication platforms
41
+ - twitter (x.com) posting by api
42
+ - bluesky posting
43
+ - tumblr posting by api
44
+ - blogger posting by mail
45
+ - facebook posting
46
+ - mastodon posting
47
+
48
+ #### Communication platforms
49
+ - discord
50
+ - telegram
51
+ - matrix
52
+ - ~whatsapp~
53
+ - ~signal~
54
+
55
+ #### Additional functionality
56
+ - email client
57
+ - ~rss feed receiver~
58
+ - ~phone manage by adb~
59
+ - webhooks - standard linkings with services like gitlab, github, discord etc.
60
+ - ntfy notifications
61
+ - nats.io
62
+
63
+ ## How it is built
64
+
65
+ One core (model, view, controller) and namespaces on it: **plugins** (one
66
+ per platform), **methods** (what amoorion can do), and the faces that expose
67
+ the methods: **CLI**, **HTTP API**, **MCP** server for AI agents, and a
68
+ **daemon** for long-running services. Secrets come only from a **vault**
69
+ (Bitwarden / Vaultwarden, HashiCorp Vault / OpenBao, or a KeePassXC file);
70
+ `.env` holds just the link to it. Each platform is its own package in
71
+ `packages/` (a uv workspace). Details: [docs/structure.md](docs/structure.md), writing a plugin:
72
+ [docs/plugins.md](docs/plugins.md).
73
+
74
+ ## Usage
75
+
76
+ ```sh
77
+ uv sync
78
+ cp example.env .env # the link to the vault
79
+
80
+ uv run amoorion # asks step by step what to do (or: amoorion -i)
81
+ uv run amoorion plugins # platforms and what they can do
82
+ uv run amoorion accounts # which have their secrets in the vault
83
+ uv run amoorion publish --text "hello" --to dryrun bluesky
84
+ uv run amoorion --json read --platform bluesky
85
+ uv run amoorion reply --platform bluesky --to "at://…" --text "thanks"
86
+ uv run amoorion inbox # everything read or received so far
87
+ uv run task help # help of every command
88
+
89
+ uv run amoorion api # HTTP API on 127.0.0.1:8000
90
+ uv run amoorion mcp # MCP server (stdio) for agents
91
+ uv run amoorion daemon # long-running services
92
+ ```
93
+
94
+ The API needs the secret `api/token` in the vault (sent as `Authorization:
95
+ Bearer`), and every hook its own secret; without them they refuse calls.
96
+ Where each backend keeps a secret: [docs/structure.md](docs/structure.md#vault).
97
+ Each platform's secrets are listed in its package's README, e.g.
98
+ [packages/amoorion-bluesky](packages/amoorion-bluesky/README.md).
99
+
100
+ Run locally first; a Dockerfile and compose come next (phase 6 of the plan).
101
+
102
+ ## Development
103
+
104
+ ```sh
105
+ uv run task init # tools, git hooks, .env, then check
106
+ uv run task check # tests, YAML, commit messages, docs (= CI)
107
+ uv run task docs:build # the docs site into docs/_build/html
108
+ uv run task plugin:new -- <platform> <Class> "<description>" # a new platform
109
+ uv run task test:live -- bluesky mastodon # one real test post on each platform named
110
+ ```
111
+
112
+ Examples of usage: Jupyter notebooks in [examples/](examples/)
113
+ (`uv run task examples:build` makes marimo notebooks and pages of them). The docs
114
+ (this README, the guides, the plan and an API reference generated from the
115
+ code) are built by Sphinx from [docs/](docs/) for Read the Docs.
116
+ Plan and open questions: [docs/devlog/](docs/devlog/). Guide for agents and
117
+ contributors: [AGENTS.md](AGENTS.md).
118
+
119
+ ## License
120
+
121
+ Free, under the MIT License, © 2024–present Sett Sarverott:
122
+ [LICENSE](LICENSE), https://sett.mit-license.org
@@ -0,0 +1,102 @@
1
+ # amoorion
2
+ Universal tool for administration of chatting and communication platforms
3
+
4
+ ---
5
+
6
+ ## **EARLY STAGE OF PROJECT**
7
+
8
+ The core works end to end, with plugins for Bluesky, Mastodon, X, Tumblr,
9
+ Blogger, Facebook, Discord (bot or webhook), Telegram and Matrix, tested
10
+ only against fakes of their APIs so far, not yet against real accounts. `dryrun` sends nothing, for trying.
11
+ Plan: [docs/devlog/VISION.md](docs/devlog/VISION.md).
12
+
13
+ ---
14
+
15
+ ## What it will do
16
+
17
+ Write once, post everywhere, read and answer from one place, and link it
18
+ with the services around it.
19
+
20
+ #### Publication platforms
21
+ - twitter (x.com) posting by api
22
+ - bluesky posting
23
+ - tumblr posting by api
24
+ - blogger posting by mail
25
+ - facebook posting
26
+ - mastodon posting
27
+
28
+ #### Communication platforms
29
+ - discord
30
+ - telegram
31
+ - matrix
32
+ - ~whatsapp~
33
+ - ~signal~
34
+
35
+ #### Additional functionality
36
+ - email client
37
+ - ~rss feed receiver~
38
+ - ~phone manage by adb~
39
+ - webhooks - standard linkings with services like gitlab, github, discord etc.
40
+ - ntfy notifications
41
+ - nats.io
42
+
43
+ ## How it is built
44
+
45
+ One core (model, view, controller) and namespaces on it: **plugins** (one
46
+ per platform), **methods** (what amoorion can do), and the faces that expose
47
+ the methods: **CLI**, **HTTP API**, **MCP** server for AI agents, and a
48
+ **daemon** for long-running services. Secrets come only from a **vault**
49
+ (Bitwarden / Vaultwarden, HashiCorp Vault / OpenBao, or a KeePassXC file);
50
+ `.env` holds just the link to it. Each platform is its own package in
51
+ `packages/` (a uv workspace). Details: [docs/structure.md](docs/structure.md), writing a plugin:
52
+ [docs/plugins.md](docs/plugins.md).
53
+
54
+ ## Usage
55
+
56
+ ```sh
57
+ uv sync
58
+ cp example.env .env # the link to the vault
59
+
60
+ uv run amoorion # asks step by step what to do (or: amoorion -i)
61
+ uv run amoorion plugins # platforms and what they can do
62
+ uv run amoorion accounts # which have their secrets in the vault
63
+ uv run amoorion publish --text "hello" --to dryrun bluesky
64
+ uv run amoorion --json read --platform bluesky
65
+ uv run amoorion reply --platform bluesky --to "at://…" --text "thanks"
66
+ uv run amoorion inbox # everything read or received so far
67
+ uv run task help # help of every command
68
+
69
+ uv run amoorion api # HTTP API on 127.0.0.1:8000
70
+ uv run amoorion mcp # MCP server (stdio) for agents
71
+ uv run amoorion daemon # long-running services
72
+ ```
73
+
74
+ The API needs the secret `api/token` in the vault (sent as `Authorization:
75
+ Bearer`), and every hook its own secret; without them they refuse calls.
76
+ Where each backend keeps a secret: [docs/structure.md](docs/structure.md#vault).
77
+ Each platform's secrets are listed in its package's README, e.g.
78
+ [packages/amoorion-bluesky](packages/amoorion-bluesky/README.md).
79
+
80
+ Run locally first; a Dockerfile and compose come next (phase 6 of the plan).
81
+
82
+ ## Development
83
+
84
+ ```sh
85
+ uv run task init # tools, git hooks, .env, then check
86
+ uv run task check # tests, YAML, commit messages, docs (= CI)
87
+ uv run task docs:build # the docs site into docs/_build/html
88
+ uv run task plugin:new -- <platform> <Class> "<description>" # a new platform
89
+ uv run task test:live -- bluesky mastodon # one real test post on each platform named
90
+ ```
91
+
92
+ Examples of usage: Jupyter notebooks in [examples/](examples/)
93
+ (`uv run task examples:build` makes marimo notebooks and pages of them). The docs
94
+ (this README, the guides, the plan and an API reference generated from the
95
+ code) are built by Sphinx from [docs/](docs/) for Read the Docs.
96
+ Plan and open questions: [docs/devlog/](docs/devlog/). Guide for agents and
97
+ contributors: [AGENTS.md](AGENTS.md).
98
+
99
+ ## License
100
+
101
+ Free, under the MIT License, © 2024–present Sett Sarverott:
102
+ [LICENSE](LICENSE), https://sett.mit-license.org
@@ -0,0 +1,127 @@
1
+ [project]
2
+ name = "amoorion"
3
+ version = "0.3.0"
4
+ description = "Universal tool for administration of chatting and communication platforms"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.12"
9
+ dependencies = [
10
+ "fastapi>=0.162.0",
11
+ "mcp[cli]>=2.3.0",
12
+ "pydantic>=2.14.1",
13
+ "python-dotenv>=1.2.4",
14
+ "requests>=2.34.2",
15
+ "rich>=15.0.0",
16
+ "uvicorn>=0.54.0",
17
+ ]
18
+
19
+ [[project.authors]]
20
+ name = "Sett Sarverott"
21
+ email = "sett@sarverott.com"
22
+
23
+ [project.scripts]
24
+ amoorion = "amoorion:main"
25
+
26
+ [project.entry-points."amoorion.plugins"]
27
+ dryrun = "amoorion.plugins.dryrun:DryRun"
28
+
29
+ [project.optional-dependencies]
30
+ keepassxc = ["pykeepass>=4.2.0"]
31
+
32
+ [build-system]
33
+ requires = ["uv_build>=0.12.20,<0.13.0"]
34
+ build-backend = "uv_build"
35
+
36
+ [dependency-groups]
37
+ dev = [
38
+ "amoorion-blogger",
39
+ "amoorion-bluesky",
40
+ "amoorion-dev",
41
+ "amoorion-discord",
42
+ "amoorion-facebook",
43
+ "amoorion-mastodon",
44
+ "amoorion-matrix",
45
+ "amoorion-telegram",
46
+ "amoorion-tumblr",
47
+ "amoorion-x",
48
+ "commitizen>=4.19.3",
49
+ "go-task-bin>=3.54.0",
50
+ "httpx>=0.28.1",
51
+ "ipykernel>=7.4.0",
52
+ "lefthook>=2.2.2",
53
+ "nbclient>=0.11.0",
54
+ "nbformat>=5.11.1",
55
+ "pykeepass>=4.2.0",
56
+ "pytest>=9.1.1",
57
+ "yamllint>=1.38.0",
58
+ ]
59
+ examples = ["marimo>=0.25.2"]
60
+
61
+ [tool.commitizen]
62
+ name = "cz_conventional_commits"
63
+ version_provider = "uv"
64
+ tag_format = "v$version"
65
+ major_version_zero = true
66
+ version_files = [
67
+ "packages/amoorion-blogger/pyproject.toml:^version",
68
+ "packages/amoorion-bluesky/pyproject.toml:^version",
69
+ "packages/amoorion-discord/pyproject.toml:^version",
70
+ "packages/amoorion-facebook/pyproject.toml:^version",
71
+ "packages/amoorion-mastodon/pyproject.toml:^version",
72
+ "packages/amoorion-matrix/pyproject.toml:^version",
73
+ "packages/amoorion-telegram/pyproject.toml:^version",
74
+ "packages/amoorion-tumblr/pyproject.toml:^version",
75
+ "packages/amoorion-x/pyproject.toml:^version",
76
+ ]
77
+ pre_bump_hooks = ["uv lock"]
78
+
79
+ [tool.pytest.ini_options]
80
+ testpaths = [
81
+ "tests",
82
+ "packages",
83
+ "tools",
84
+ ]
85
+ addopts = [
86
+ "--import-mode=importlib",
87
+ "-m",
88
+ "not live",
89
+ ]
90
+ markers = ["live: posts on a real platform account (task test:live -- <platform>…)"]
91
+
92
+ [tool.uv.workspace]
93
+ members = [
94
+ "packages/*",
95
+ "tools/*",
96
+ "docs",
97
+ ]
98
+
99
+ [tool.uv.sources.amoorion-bluesky]
100
+ workspace = true
101
+
102
+ [tool.uv.sources.amoorion-mastodon]
103
+ workspace = true
104
+
105
+ [tool.uv.sources.amoorion-x]
106
+ workspace = true
107
+
108
+ [tool.uv.sources.amoorion-tumblr]
109
+ workspace = true
110
+
111
+ [tool.uv.sources.amoorion-blogger]
112
+ workspace = true
113
+
114
+ [tool.uv.sources.amoorion-facebook]
115
+ workspace = true
116
+
117
+ [tool.uv.sources.amoorion-discord]
118
+ workspace = true
119
+
120
+ [tool.uv.sources.amoorion-telegram]
121
+ workspace = true
122
+
123
+ [tool.uv.sources.amoorion-matrix]
124
+ workspace = true
125
+
126
+ [tool.uv.sources.amoorion-dev]
127
+ workspace = true
@@ -0,0 +1,111 @@
1
+ [project]
2
+ name = "amoorion"
3
+ version = "0.3.0"
4
+ description = "Universal tool for administration of chatting and communication platforms"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ authors = [
9
+ { name = "Sett Sarverott", email = "sett@sarverott.com" }
10
+ ]
11
+ requires-python = ">=3.12"
12
+ dependencies = [
13
+ "fastapi>=0.162.0",
14
+ "mcp[cli]>=2.3.0",
15
+ "pydantic>=2.14.1",
16
+ "python-dotenv>=1.2.4",
17
+ "requests>=2.34.2",
18
+ "rich>=15.0.0",
19
+ "uvicorn>=0.54.0",
20
+ ]
21
+
22
+ [project.scripts]
23
+ amoorion = "amoorion:main"
24
+
25
+ # Platform plugins, found through this group; the packages in packages/ add theirs the same way.
26
+ [project.entry-points."amoorion.plugins"]
27
+ dryrun = "amoorion.plugins.dryrun:DryRun"
28
+
29
+ [project.optional-dependencies]
30
+ keepassxc = [
31
+ "pykeepass>=4.2.0",
32
+ ]
33
+
34
+ [build-system]
35
+ requires = ["uv_build>=0.12.20,<0.13.0"]
36
+ build-backend = "uv_build"
37
+
38
+ [dependency-groups]
39
+ dev = [
40
+ "amoorion-blogger",
41
+ "amoorion-bluesky",
42
+ "amoorion-dev",
43
+ "amoorion-discord",
44
+ "amoorion-facebook",
45
+ "amoorion-mastodon",
46
+ "amoorion-matrix",
47
+ "amoorion-telegram",
48
+ "amoorion-tumblr",
49
+ "amoorion-x",
50
+ "commitizen>=4.19.3",
51
+ "go-task-bin>=3.54.0",
52
+ "httpx>=0.28.1",
53
+ "ipykernel>=7.4.0",
54
+ "lefthook>=2.2.2",
55
+ "nbclient>=0.11.0",
56
+ "nbformat>=5.11.1",
57
+ "pykeepass>=4.2.0",
58
+ "pytest>=9.1.1",
59
+ "yamllint>=1.38.0",
60
+ ]
61
+ examples = [
62
+ "marimo>=0.25.2",
63
+ ]
64
+
65
+ [tool.commitizen]
66
+ name = "cz_conventional_commits"
67
+ version_provider = "uv"
68
+ tag_format = "v$version"
69
+ # CHANGELOG.md is written by `amoorion-dev version final`, not on every bump.
70
+ major_version_zero = true
71
+ # Every package of the workspace is released with the same version.
72
+ version_files = [
73
+ "packages/amoorion-blogger/pyproject.toml:^version",
74
+ "packages/amoorion-bluesky/pyproject.toml:^version",
75
+ "packages/amoorion-discord/pyproject.toml:^version",
76
+ "packages/amoorion-facebook/pyproject.toml:^version",
77
+ "packages/amoorion-mastodon/pyproject.toml:^version",
78
+ "packages/amoorion-matrix/pyproject.toml:^version",
79
+ "packages/amoorion-telegram/pyproject.toml:^version",
80
+ "packages/amoorion-tumblr/pyproject.toml:^version",
81
+ "packages/amoorion-x/pyproject.toml:^version",
82
+ ]
83
+ # uv.lock records the packages' versions too.
84
+ pre_bump_hooks = ["uv lock"]
85
+
86
+ [tool.pytest.ini_options]
87
+ testpaths = ["tests", "packages", "tools"]
88
+ # Test modules of several packages may share a name. Live tests post for
89
+ # real: left out unless asked for (task test:live).
90
+ addopts = ["--import-mode=importlib", "-m", "not live"]
91
+ markers = ["live: posts on a real platform account (task test:live -- <platform>…)"]
92
+
93
+ # Platform plugins, one package each (decision Q10).
94
+ [tool.uv.workspace]
95
+ members = [
96
+ "packages/*",
97
+ "tools/*",
98
+ "docs",
99
+ ]
100
+
101
+ [tool.uv.sources]
102
+ amoorion-bluesky = { workspace = true }
103
+ amoorion-mastodon = { workspace = true }
104
+ amoorion-x = { workspace = true }
105
+ amoorion-tumblr = { workspace = true }
106
+ amoorion-blogger = { workspace = true }
107
+ amoorion-facebook = { workspace = true }
108
+ amoorion-discord = { workspace = true }
109
+ amoorion-telegram = { workspace = true }
110
+ amoorion-matrix = { workspace = true }
111
+ amoorion-dev = { workspace = true }
@@ -0,0 +1,12 @@
1
+ """amoorion: administration of chatting and communication platforms."""
2
+
3
+ from importlib.metadata import version
4
+
5
+ # From the installed package, so the docs, the API and `--version` never lag behind pyproject.toml.
6
+ __version__ = version("amoorion")
7
+
8
+
9
+ def main() -> int:
10
+ from amoorion.cli import main
11
+
12
+ return main()
@@ -0,0 +1,5 @@
1
+ """HTTP API: the methods and the hooks over HTTP."""
2
+
3
+ from amoorion.api.base import create_app, serve
4
+
5
+ __all__ = ["create_app", "serve"]
@@ -0,0 +1,5 @@
1
+ from amoorion.cli import main
2
+
3
+ # Only when run (python -m); importing it, as the API reference does, starts nothing.
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main(["api"]))
@@ -0,0 +1,87 @@
1
+ """API: `POST /methods/<name>` with the method's params as the body,
2
+ `POST /hooks/<name>` for incoming webhooks (kept in the inbox), `GET /` for
3
+ what is there.
4
+
5
+ Method calls need `Authorization: Bearer <api/token from the vault>`; without
6
+ that secret in the vault every method call is refused. Hooks verify
7
+ themselves (see amoorion.hooks).
8
+ """
9
+
10
+ import argparse
11
+ import hmac
12
+ from typing import Annotated, Any
13
+
14
+ from fastapi import Depends, FastAPI, HTTPException, Request
15
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
16
+
17
+ from amoorion import __version__
18
+ from amoorion.core import AmoorionError, JsonView
19
+ from amoorion.hooks import HOOKS, Forbidden
20
+ from amoorion.inbox import Inbox
21
+ from amoorion.methods import METHODS, Method
22
+ from amoorion.vault import Vault, open_vault
23
+
24
+ bearer = HTTPBearer(auto_error=False)
25
+
26
+
27
+ def create_app(vault: Vault | None = None, inbox: Inbox | None = None) -> FastAPI:
28
+ vault = vault or open_vault()
29
+ inbox = inbox or Inbox()
30
+ app = FastAPI(title="amoorion", version=__version__)
31
+
32
+ def authorized(credentials: Annotated[HTTPAuthorizationCredentials | None, Depends(bearer)]) -> None:
33
+ try:
34
+ token = vault.get("api/token")
35
+ except AmoorionError:
36
+ raise HTTPException(503, "no api/token in the vault: method calls are off") from None
37
+ if credentials is None or not hmac.compare_digest(credentials.credentials.encode(), token.encode()):
38
+ raise HTTPException(401, "wrong or missing bearer token")
39
+
40
+ @app.get("/")
41
+ def index() -> dict[str, Any]:
42
+ return {"name": "amoorion", "version": __version__, "methods": METHODS.names(), "hooks": HOOKS.names()}
43
+
44
+ def add_method(method: type[Method]) -> None:
45
+ Params = method.Params
46
+
47
+ def call(params: Params) -> Any: # type: ignore[valid-type]
48
+ try:
49
+ return JsonView().render(method(vault, inbox=inbox).run(params))
50
+ except AmoorionError as error:
51
+ raise HTTPException(400, str(error)) from None
52
+
53
+ app.post(
54
+ f"/methods/{method.name}",
55
+ summary=method.description,
56
+ dependencies=[Depends(authorized)],
57
+ name=method.name,
58
+ )(call)
59
+
60
+ for name in METHODS.names():
61
+ add_method(METHODS.get(name))
62
+
63
+ @app.post("/hooks/{name}")
64
+ async def hook(name: str, request: Request) -> Any:
65
+ if name not in HOOKS:
66
+ raise HTTPException(404, f"no hook named {name!r}")
67
+ body = await request.body()
68
+ try:
69
+ payload = await request.json()
70
+ except ValueError:
71
+ raise HTTPException(400, "body is not JSON") from None
72
+ try:
73
+ message = HOOKS.get(name)(vault).handle(body, request.headers, payload)
74
+ except Forbidden as error:
75
+ raise HTTPException(403, str(error)) from None
76
+ except AmoorionError as error:
77
+ raise HTTPException(400, str(error)) from None
78
+ [kept] = inbox.keep([message])
79
+ return JsonView().render(kept)
80
+
81
+ return app
82
+
83
+
84
+ def serve(args: argparse.Namespace) -> None:
85
+ import uvicorn
86
+
87
+ uvicorn.run(create_app(), host=args.host, port=args.port)
@@ -0,0 +1,5 @@
1
+ """Command line: one subcommand per method, plus the servers."""
2
+
3
+ from amoorion.cli.base import RichView, build_parser, main
4
+
5
+ __all__ = ["RichView", "build_parser", "main"]
@@ -0,0 +1,5 @@
1
+ from amoorion.cli.base import main
2
+
3
+ # Only when run (python -m); importing it, as the API reference does, starts nothing.
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main())