onbot 0.1.0.dev0__py3-none-any.whl

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.
@@ -0,0 +1,77 @@
1
+ """Versioned onbot room-state schemas.
2
+
3
+ The bot has no database; it persists its own bookkeeping as **custom Matrix room-state events**
4
+ (AD-1 keeps this idea). Event types are namespaced with the reversed server name, e.g.
5
+ ``org.company.onbot.group_room``. Legacy stored these as bare, unversioned dicts; we give them
6
+ explicit, validated pydantic schemas carrying a ``schema_version`` so future migrations are possible.
7
+
8
+ This module is pure (no I/O): the reconciler reads/writes these via the Matrix client (Phase 4).
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from enum import StrEnum
14
+ from typing import Any, Literal
15
+
16
+ from pydantic import BaseModel, Field
17
+
18
+ SCHEMA_VERSION = 1
19
+
20
+
21
+ class OnbotRoomType(StrEnum):
22
+ space = "space"
23
+ group_room = "group_room"
24
+ direct_room = "direct_room"
25
+
26
+
27
+ def event_type_name(server_name: str, room_type: OnbotRoomType | str) -> str:
28
+ """Fully-qualified custom state event type, e.g. ``org.company.onbot.group_room``.
29
+
30
+ Mirrors the legacy reversed-domain scheme so existing rooms keep matching.
31
+ """
32
+ rt = room_type.value if isinstance(room_type, OnbotRoomType) else room_type
33
+ reversed_domain = ".".join(reversed(server_name.split(".")))
34
+ return f"{reversed_domain}.onbot.{rt}"
35
+
36
+
37
+ class _OnbotRoomState(BaseModel):
38
+ schema_version: int = SCHEMA_VERSION
39
+ authentik_server: str | None = None
40
+ avatar_source_url: str | None = None
41
+
42
+
43
+ class SpaceRoomState(_OnbotRoomState):
44
+ room_type: Literal[OnbotRoomType.space] = OnbotRoomType.space
45
+
46
+
47
+ class GroupRoomState(_OnbotRoomState):
48
+ room_type: Literal[OnbotRoomType.group_room] = OnbotRoomType.group_room
49
+ group_id: str
50
+
51
+
52
+ class DirectRoomState(_OnbotRoomState):
53
+ room_type: Literal[OnbotRoomType.direct_room] = OnbotRoomType.direct_room
54
+ user_id: str
55
+ marked_for_disabling_timestamp: float | None = None
56
+ disabled_user_timestamp: float | None = None
57
+ welcome_messages_sent: dict[str, str] = Field(default_factory=dict)
58
+
59
+
60
+ AnyRoomState = SpaceRoomState | GroupRoomState | DirectRoomState
61
+
62
+ _STATE_MODEL_BY_TYPE: dict[OnbotRoomType, type[AnyRoomState]] = {
63
+ OnbotRoomType.space: SpaceRoomState,
64
+ OnbotRoomType.group_room: GroupRoomState,
65
+ OnbotRoomType.direct_room: DirectRoomState,
66
+ }
67
+
68
+
69
+ def parse_room_state(room_type: OnbotRoomType, content: dict[str, Any]) -> AnyRoomState:
70
+ """Validate raw state-event content into the matching schema for ``room_type``."""
71
+ model = _STATE_MODEL_BY_TYPE[room_type]
72
+ return model.model_validate(content)
73
+
74
+
75
+ def dump_room_state(state: AnyRoomState) -> dict[str, Any]:
76
+ """Serialise a room-state model to JSON-able content for a Matrix state event."""
77
+ return state.model_dump(mode="json")
onbot/utils.py ADDED
@@ -0,0 +1,61 @@
1
+ """Small, dependency-free helpers.
2
+
3
+ Ported from legacy ``onbot/utils.py`` — we keep the nested-dict helpers (used pervasively to read
4
+ dotted attribute paths out of Authentik objects) and drop the legacy ``synchronize_async_helper``
5
+ sync/async bridge (AD-7: async everything) and the ``requests``-based media download (Phase 6,
6
+ authenticated media). The ``Any``-as-sentinel hack is replaced with an explicit ``_MISSING`` marker.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections.abc import Sequence
12
+ from typing import Any, Final
13
+
14
+
15
+ class _Missing:
16
+ """Sentinel distinguishing 'no fallback given' from a ``None`` fallback."""
17
+
18
+ __slots__ = ()
19
+
20
+ def __repr__(self) -> str: # pragma: no cover - debugging aid
21
+ return "<MISSING>"
22
+
23
+
24
+ _MISSING: Final = _Missing()
25
+
26
+
27
+ def get_nested_dict_val_by_path(
28
+ data: dict[str, Any],
29
+ key_path: Sequence[str],
30
+ fallback_val: Any = _MISSING,
31
+ ) -> Any:
32
+ """Read a nested value via a key path (e.g. ``["attributes", "matrix_name"]``).
33
+
34
+ Raises ``KeyError`` if the path is absent and no ``fallback_val`` was provided.
35
+ """
36
+ current: Any = data
37
+ for key in key_path:
38
+ try:
39
+ current = current[key]
40
+ except KeyError, TypeError:
41
+ if fallback_val is _MISSING:
42
+ raise KeyError(key) from None
43
+ return fallback_val
44
+ return current
45
+
46
+
47
+ def dict_has_nested_attr(
48
+ data: dict[str, Any],
49
+ key_path: Sequence[str],
50
+ *,
51
+ must_have_val: bool = False,
52
+ ) -> bool:
53
+ """Return whether ``data`` has the nested ``key_path`` (and a truthy value if ``must_have_val``)."""
54
+ current: Any = data
55
+ for key in key_path:
56
+ if not isinstance(current, dict) or key not in current:
57
+ return False
58
+ current = current[key]
59
+ if must_have_val:
60
+ return bool(current)
61
+ return True
@@ -0,0 +1,241 @@
1
+ Metadata-Version: 2.1
2
+ Name: onbot
3
+ Version: 0.1.0.dev0
4
+ Summary: Keep a Matrix (Synapse) homeserver in sync with Authentik and onboard new users.
5
+ Keywords: matrix,synapse,authentik,mas,bot,onboarding
6
+ Author: DZD
7
+ License: MIT
8
+ Classifier: Programming Language :: Python :: 3.14
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Project-URL: Homepage, https://github.com/DZD-eV-Diabetes-Research/matrix-synapse-authentik-onbaording-bot
11
+ Requires-Python: >=3.14
12
+ Requires-Dist: httpx>=0.27
13
+ Requires-Dist: tenacity>=9.0
14
+ Requires-Dist: pydantic>=2.7
15
+ Requires-Dist: pydantic-settings>=2.3
16
+ Requires-Dist: pyyaml>=6.0
17
+ Description-Content-Type: text/markdown
18
+
19
+ # Onbot
20
+
21
+ A bot that keeps a **Matrix (Synapse)** homeserver continuously in sync with an
22
+ **[Authentik](https://goauthentik.io/)** identity provider and onboards every new user into the
23
+ right rooms with a friendly welcome. Authentik is the source of truth; Matrix mirrors it
24
+ (group → room, group membership → room membership, power levels), and new users get guided in with a
25
+ 1:1 welcome DM.
26
+
27
+ Onbot targets **Matrix 2.0**: it assumes a [**Matrix Authentication Service
28
+ (MAS)**](https://element-hq.github.io/matrix-authentication-service/) deployment with Authentik as
29
+ an *upstream identity provider*, uses authenticated media, and drives the Client-Server and admin
30
+ APIs over a single async HTTP client (no `matrix-nio`).
31
+
32
+ See [`GOALS.md`](GOALS.md) for intent, [`BATTLE_PLAN.md`](BATTLE_PLAN.md) for the build plan, and
33
+ [`docs/adr/`](docs/adr/) for the architecture decisions.
34
+
35
+ > ⚠️ **Release blocker (maintainer):** the Phase 1 security items are **not done** — leaked
36
+ > credentials in git history still need rotating and the history scrubbing
37
+ > ([`BATTLE_PLAN.md`](BATTLE_PLAN.md) §5 Phase 1). **Do not publish an image or tag a release until
38
+ > those are complete.** The packaging below is ready; the security hand-off is the gate.
39
+
40
+ ---
41
+
42
+ ## How it works — the MAS auth topology
43
+
44
+ The auth chain is **Matrix client → MAS → Authentik** (ADR-[0006](docs/adr/0006-auth-topology-mas-authentik.md)):
45
+
46
+ ```
47
+ logs in via upstream IdP
48
+ Matrix client ───────────────▶ MAS ◀─────────────────────── Authentik
49
+ ▲ │ (provisions Matrix accounts (source of truth:
50
+ │ │ on first login) users & groups)
51
+ │ welcome DM, │
52
+ │ room membership ┌────┴─────┐
53
+ └──────────────────────│ Onbot │── reads users/groups ──▶ Authentik API
54
+ └────┬─────┘
55
+ └── Synapse Admin API + CS API ──▶ Synapse ◀─ MAS
56
+ ```
57
+
58
+ Consequences that shape how you configure Onbot:
59
+
60
+ - **Onbot does not create accounts.** MAS auto-provisions a Matrix account the first time a user
61
+ logs in through Authentik. Onbot's job is **projection**: turn Authentik groups into rooms, group
62
+ membership into room membership, and group/role attributes into power levels — plus the
63
+ quarantined offboarding lifecycle.
64
+ - **The MXID localpart contract is critical.** Onbot computes a user's MXID
65
+ (`@<localpart>:server_name`) from an Authentik attribute, and it **must match the localpart
66
+ template MAS uses** when it provisions accounts from the same Authentik claim. Set
67
+ [`sync_authentik_users_with_matrix_rooms.authentik_username_mapping_attribute`](docs/CONFIG_REFERENCE.md)
68
+ to agree with MAS — get it wrong and Onbot's computed MXIDs won't match the real accounts, so
69
+ nobody gets added to rooms. (Verified by the integration suite's localpart-contract test.)
70
+ - **Lifecycle enforcement requires MAS.** When Authentik disables a user, MAS blocks *new* logins
71
+ but **existing Matrix sessions keep working**, and the *Synapse* admin API cannot revoke a
72
+ MAS-issued session — only MAS can (ADR-[0005](docs/adr/0005-quarantine-lifecycle.md), §7 Q1, proven
73
+ empirically). So to actually offboard a disabled user you must configure the
74
+ [`mas_admin`](docs/CONFIG_REFERENCE.md) block. Without it, offboarding is a **no-op against live
75
+ sessions** (it silently fails to revoke).
76
+
77
+ ---
78
+
79
+ ## Configuration
80
+
81
+ Configuration is a single YAML file validated by a [pydantic-settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/)
82
+ model ([`onbot/config.py`](onbot/config.py)). Every setting can also be supplied (or overridden) via
83
+ an environment variable: prefix `ONBOT_`, nest with `__`. E.g.
84
+ `ONBOT_SYNAPSE_SERVER__BOT_ACCESS_TOKEN=syt_…`.
85
+
86
+ - **Full reference:** [`docs/CONFIG_REFERENCE.md`](docs/CONFIG_REFERENCE.md) — every field, its type,
87
+ default, description and `ONBOT_*` env-var name.
88
+ - **Annotated template:** [`config.example.yml`](config.example.yml) — a commented, fillable YAML
89
+ template. Copy it to `config.yml` and fill in the required values.
90
+
91
+ Both are **generated from the model** (with [psyplus](https://pypi.org/project/psyplus/)) and kept in
92
+ sync by CI — regenerate after editing `onbot/config.py`:
93
+
94
+ ```bash
95
+ pdm run gen-config-docs # rewrite docs/CONFIG_REFERENCE.md + config.example.yml
96
+ pdm run check-config-docs # fail if they drift from the model (runs in CI)
97
+ ```
98
+
99
+ > 🔐 **Never commit a real config.** `config*.yml` is gitignored (only `config.example.yml` is
100
+ > tracked) and the Docker image carries no secrets — provide config at runtime.
101
+
102
+ ### Bot credentials (pick one)
103
+
104
+ Onbot authenticates to Synapse as a bot user. Under MAS, choose one of:
105
+
106
+ | Option | Field | When |
107
+ |---|---|---|
108
+ | **Compatibility token** | `synapse_server.bot_access_token` | Near-term. Issue with `mas-cli manage issue-compatibility-token`. Provide the bare token. |
109
+ | **OAuth2 client-credentials** | `synapse_server.oauth2` | Forward-looking. The bot is a confidential MAS client and refreshes tokens automatically. |
110
+
111
+ Provide **exactly one**. The same identity drives both the Synapse Admin API and the Client-Server
112
+ API.
113
+
114
+ ### Minimal `config.yml`
115
+
116
+ ```yaml
117
+ synapse_server:
118
+ server_name: company.org # your Matrix domain (the part after the ':')
119
+ server_url: https://internal.matrix # how the bot reaches Synapse (internal URL is fine)
120
+ bot_user_id: "@welcome-bot:company.org"
121
+ bot_access_token: syt_REPLACE_ME # or use an `oauth2:` block instead
122
+
123
+ authentik_server:
124
+ url: https://authentik.company.org/
125
+ api_key: REPLACE_ME # Authentik API token
126
+
127
+ # Required to enforce offboarding under MAS (omit on non-MAS deployments):
128
+ mas_admin:
129
+ url: https://auth.company.org # the MAS base URL
130
+ client_id: REPLACE_ME # a MAS admin client (in policy.data.admin_clients)
131
+ client_secret: REPLACE_ME
132
+
133
+ sync_authentik_users_with_matrix_rooms:
134
+ authentik_username_mapping_attribute: username # MUST agree with MAS's localpart template
135
+ ```
136
+
137
+ See [`config.example.yml`](config.example.yml) for everything else (room mapping rules, power
138
+ levels, welcome messages, the dry-run lifecycle defaults, ignore lists, …).
139
+
140
+ ---
141
+
142
+ ## Run with Docker
143
+
144
+ The published image runs as a non-root user and ships only runtime dependencies (no crypto stack —
145
+ the bot operates outside encrypted rooms, ADR-[0009](docs/adr/0009-e2ee-stance.md)).
146
+
147
+ ```bash
148
+ docker run --rm \
149
+ -v "$PWD/config.yml:/config/config.yml:ro" \
150
+ ghcr.io/dzd-ev-diabetes-research/matrix-synapse-authentik-onbaording-bot:latest
151
+ ```
152
+
153
+ The image defaults to `ONBOT_CONFIG_FILE_PATH=/config/config.yml` and the `run` command. It has a
154
+ built-in `HEALTHCHECK` that calls `onbot healthcheck` (see below).
155
+
156
+ ### docker-compose
157
+
158
+ ```yaml
159
+ services:
160
+ onbot:
161
+ image: ghcr.io/dzd-ev-diabetes-research/matrix-synapse-authentik-onbaording-bot:latest
162
+ restart: unless-stopped
163
+ volumes:
164
+ - ./config.yml:/config/config.yml:ro
165
+ # Or skip the file and supply everything via env (no secrets on disk):
166
+ # environment:
167
+ # ONBOT_SYNAPSE_SERVER__SERVER_NAME: company.org
168
+ # ONBOT_SYNAPSE_SERVER__BOT_ACCESS_TOKEN: ${ONBOT_BOT_TOKEN}
169
+ ```
170
+
171
+ ### CLI commands
172
+
173
+ ```
174
+ onbot run # long-running service: reconcile loop + event-driven onboarding (default)
175
+ onbot reconcile-once # one idempotent reconcile pass, then exit
176
+ onbot generate-config # print a minimal config template (use config.example.yml for the rich one)
177
+ onbot healthcheck # probe Synapse/Authentik/MAS with the real credentials; exit 0 healthy, 1 not
178
+ ```
179
+
180
+ `onbot healthcheck` is what the container's `HEALTHCHECK` runs: it issues one authenticated request
181
+ to the Matrix CS API (`/whoami`), the Synapse admin API, the Authentik API, and — when `mas_admin`
182
+ is configured — the MAS admin API, and exits non-zero if any is unreachable or rejects the
183
+ credentials.
184
+
185
+ ---
186
+
187
+ ## Develop
188
+
189
+ Requires **Python 3.14** and **[PDM](https://pdm-project.org/)**.
190
+
191
+ ```bash
192
+ pdm install # create the venv and install deps (incl. dev + docs)
193
+ pdm run pre-commit install # enable lint + secret-scan hooks
194
+ ```
195
+
196
+ ### Run & test
197
+
198
+ ```bash
199
+ pdm run onbot --help
200
+ pdm run onbot reconcile-once
201
+
202
+ pdm run pytest -m "not integration" # fast unit + contract suite
203
+ pdm run pytest # full suite incl. live Synapse+MAS+Authentik (needs Docker)
204
+ pdm run ruff check . # lint
205
+ pdm run ruff format --check . # formatting
206
+ pdm run mypy onbot # type check
207
+ ```
208
+
209
+ The `run_*.sh` helper scripts at the repo root wrap the same PDM commands.
210
+
211
+ ### Build the image locally
212
+
213
+ ```bash
214
+ docker build -t onbot:dev .
215
+ docker run --rm onbot:dev --help
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Troubleshooting
221
+
222
+ | Symptom | Likely cause |
223
+ |---|---|
224
+ | Users never get added to rooms | The MXID localpart contract is broken — `authentik_username_mapping_attribute` doesn't match MAS's localpart template, so computed MXIDs don't exist. |
225
+ | Disabled users keep their Matrix access | `mas_admin` is not configured (the Synapse admin API can't revoke a MAS session), or lifecycle `dry_run` is still `true` (the default). |
226
+ | Nothing destructive ever happens | Expected by default — the lifecycle is quarantined (`dry_run: true`); it only logs to the `onbot.lifecycle.audit` channel until you opt in. |
227
+ | Welcome DM send fails with a 500 | The bot device isn't registered yet — Onbot registers it on startup (`ensure_device_registered`); check startup logs. |
228
+ | `healthcheck` reports a dependency FAIL | Read the per-dependency log line; it distinguishes unreachable from auth-rejected. The `matrix-cs` line also flags a token/`bot_user_id` mismatch. |
229
+ | Sliding sync unavailable | The homeserver doesn't advertise MSC4186; Onbot falls back to the reconciler signal path automatically. |
230
+
231
+ ---
232
+
233
+ ## Releasing
234
+
235
+ Versioned images are published to GHCR by the [release workflow](.github/workflows/release.yml) when
236
+ a `v*` tag is pushed. See [`CHANGELOG.md`](CHANGELOG.md) and the workflow's header comment for the
237
+ tag/version flow. **(Blocked on the Phase 1 security hand-off — see the note at the top.)**
238
+
239
+ ## License
240
+
241
+ MIT — see [`LICENSE`](LICENSE).
@@ -0,0 +1,38 @@
1
+ onbot-0.1.0.dev0.dist-info/METADATA,sha256=pI9PGj7lF4Gt2wmN_TRK6wA9KHew7B_AqeHRNyiwLJ4,11164
2
+ onbot-0.1.0.dev0.dist-info/WHEEL,sha256=VP-D4TPS230sME9Z3vb3INXvo1yt0924YRm5AOsk_dE,90
3
+ onbot-0.1.0.dev0.dist-info/entry_points.txt,sha256=DGrxm-JKvVp239mGhGJC9xY-IvC-2AjjrdUg0L6yQXs,57
4
+ onbot-0.1.0.dev0.dist-info/licenses/LICENSE,sha256=TuZlsZ-MBR0SxDZR0I8eTSMK2oI-UPKSs8Du8cSpxU0,1101
5
+ onbot/__init__.py,sha256=PCI_AaMaoMIOSBLYD-84-oi2NaJ4vv5gKcOQr89b8w0,292
6
+ onbot/__main__.py,sha256=GO4R6IprVZwwwH3glDpQrNz2_MU0BrnN75lKDIDfaPc,119
7
+ onbot/app.py,sha256=_x-hJdEUQww4rHNLPTdL7XqKAP89nx3vlWqvmBsJRak,7046
8
+ onbot/auth/__init__.py,sha256=ur6qmM7E1fNUpxw2OaVr7vIxQK_EgurweB1knp9Afj8,496
9
+ onbot/auth/token_provider.py,sha256=1RjflnWDT2zubQH-wdvb9v251jbfDnm8mJ7PHmhOPdo,5958
10
+ onbot/cli.py,sha256=iiZ5Vk0ZQYH7o0BtTGmyn7mLFTSrD3LwIpSCrpBnvsE,3048
11
+ onbot/clients/__init__.py,sha256=2bZ_RF_0vjzaLj-Q8g8HWKJaS3fjZBBQT2ni7_vE3fQ,263
12
+ onbot/clients/authentik.py,sha256=DvBVgdKX6oxe0reXJ7_FmUNfd0_op-tnW-JfGenSbM8,4812
13
+ onbot/clients/base.py,sha256=8WSzLBuYXSWJJYnN5ZAyPY-k4_H7Nex5LQr3II41vq0,9302
14
+ onbot/clients/mas_admin.py,sha256=Snc76PcnIqT4PwUs4nH4OzK6Mv4x_T1ennHI8wwiLhI,2594
15
+ onbot/clients/matrix.py,sha256=WEwwnm3q9fmbc0vPfCGFHxbgfS_84OG6HWes-jlNJ3c,19693
16
+ onbot/clients/synapse_admin.py,sha256=kfrQtfy5M5Qq-sir1U4wRWFAiszMBsw9nJWTff-V56w,7467
17
+ onbot/clients/versions.py,sha256=RzM6BilIJLwCzjT7kuo-wrzF4o00DU4nqKQruETd5iU,2668
18
+ onbot/config.py,sha256=zq5Tk9kn7n6nz5WZTGkwO0U5kO86U7XgtFX07cVRw3o,26634
19
+ onbot/events.py,sha256=MTBBZ1bcWVukZzVwI4suqgzXluQy9PTLfwyHdEX2CnM,1563
20
+ onbot/healthcheck.py,sha256=loZWenxjg4H0nR9DcxDEdPFfHKinQ0zTQQzEvNwMlXk,5530
21
+ onbot/identity.py,sha256=ggK4pqmxvjMhPwFXRSAqJIectz8P0McU2DHDMLQ9M2I,1809
22
+ onbot/lifecycle/__init__.py,sha256=E8vfTN8AOjQO6c0wb1lKK26PaYG78-Phcidn5_AkbLk,148
23
+ onbot/lifecycle/accounts.py,sha256=6gIZOMZhfpnTa8471fW-jjF735i2x7gtw8lfLSty_uY,13269
24
+ onbot/logging.py,sha256=qASVDwcQLj4xowUL3lnETp7gkMWjdtAiAQ28k5amqG4,899
25
+ onbot/media.py,sha256=Uh53B_-HePoIXxCKVLcOBfJldDVHRDTiX_Ea-3XJ4Mo,1981
26
+ onbot/models.py,sha256=GyJelGYy2_5nZLMkKNnlA_dsIIyga720bF0kootlIfk,2092
27
+ onbot/onboarding/__init__.py,sha256=y4lt5sP_HhC7nfAKFTJLEoDWTfH1AUT_PY5R_SSzniI,267
28
+ onbot/onboarding/listener.py,sha256=fEh5wNfxABth8JpSpM3DTAuStN6ef_d3hRqVGh38tiY,4024
29
+ onbot/onboarding/welcome.py,sha256=Nzo9q29m7Lhq2AUAJjzlsa5ZaDmWps6KQFcq5zjgYnE,5001
30
+ onbot/reconciler/__init__.py,sha256=3X9zZTz6v6y1CgX6LSuBs_R4JOy2g6BEJdOR2CvQfs4,179
31
+ onbot/reconciler/effectors.py,sha256=9Ys6FaPMhDQIU71FayOcog0K2U7WpgQXp4kMdWRkIN8,3499
32
+ onbot/reconciler/engine.py,sha256=05T27P_ccxmIf-dkkYWulPYFW-J8IK-TAP1-v4N4T1U,15905
33
+ onbot/reconciler/membership.py,sha256=gLuH7pIYFpCWYqG8y6xHQucyGRmfav643sCe5GV473M,1938
34
+ onbot/reconciler/power_levels.py,sha256=HKlPLNNcxnc5e_2JS0SLC13GNgYEHQ6pYWKsfXhgkks,3524
35
+ onbot/reconciler/rooms.py,sha256=kjNnul402Nwg11DCe29Ypo5c_V2oIbmE-9zq8AINS1g,5262
36
+ onbot/reconciler/state.py,sha256=vqzy6JKCm45fe7CwpNGGYjsx7Hsg91uIHgcyQjvJQ1Q,2669
37
+ onbot/utils.py,sha256=xFHB-7MfFzxj_NJ5AYx4BRf-wAbhUaSp7PlhHG0p7Rs,1839
38
+ onbot-0.1.0.dev0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: pdm-backend (2.4.9)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ onbot = onbot.cli:main
3
+
4
+ [gui_scripts]
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Deutsche Zentrum für Diabetesforschung e.V.
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.