v3.0.0 #1

Merged
zevaryx merged 55 commits from dev into main 2026-11-03 23:28:34 +00:00
Owner

New JARVIS Core for the new v3.0.0 release

New JARVIS Core for the new v3.0.0 release
"Captcha" "Config" was implicitly concatenated into "CaptchaConfig", which made `from jarvis_core.db.models import *` raise AttributeError and left Captcha and Config unexported.
None of these are used by the bot or tasks anymore. mastodon.py imported a nonexistent JARVIS_INST and backups.py was never registered.

BREAKING CHANGE: Subreddit, SubredditFollow, TwitterAccount and TwitterFollow are no longer exported or registered with Beanie.
Move metadata to the PEP 621 [project] table, dev tools to a PEP 735 dependency group, and build with uv_build. Replace poetry.lock with uv.lock and port the CI test and release jobs to uv. Dependency constraints are unchanged.
Beanie 2 drops Motor, so connect() now uses pymongo.AsyncMongoClient and returns the client. Bump aiohttp, pydantic and rich to their latest releases and require Python 3.11 (PyMongo 4.19's floor).

Drop the unused orjson, PyYAML, python-dotenv and python-dateutil dependencies and the needless setuptools<70 pin. mongomock_motor cannot drive PyMongo's async client, so database tests now run against a real MongoDB named by JARVIS_TEST_MONGO_URI and are skipped without it.

BREAKING CHANGE: requires Beanie 2 / PyMongo async and Python 3.11+; Motor clients are no longer supported.
connect() hard-coded the jarvis/jarvis_dev database and had no way to pass authSource, so it could not reach a database-scoped user whose credentials live elsewhere (e.g. admin). Both are now optional parameters; the defaults are unchanged.
SnowflakeDocument encoded Snowflake fields to str on save, but Beanie does not apply that encoder to query values, so every lookup by guild/user/channel ID with an int (including interactions.Snowflake) matched nothing. Drop the encoder so IDs are stored and queried as int64.

Snowflake now rejects values outside 0..2^63-1 (the int64 range every Discord ID fits in) and still accepts numeric strings so documents written by older versions keep loading until migrated.

BREAKING CHANGE: existing documents store IDs as strings and will not match queries until migrated (see the migration that follows).
The bot and tasks both pass admin= when creating an Unban, but the model had no such field, so pydantic silently dropped it and who lifted a ban was never stored. Optional so existing documents still load.
When @everyone has no overwrite on a channel the bot passes original_perms=None, which raised a ValidationError after the channel had already been locked, leaving it locked with no record. Make the field optional; None means "remove the overwrite on unlock".
The bot creates locks with guild= and /unlock, /lockdown end and the tasks unlock job all read lock.guild, but Lock never declared the field: it was discarded on save and every reader raised AttributeError. Optional so locks saved without it still load.
Ban.discrim was an int with no default (so it was required) while Unban.discrim was a str; ints dropped leading zeros ("0042" became 42) and Discord's "0" (no discriminator) was stored as-is. Both now share a Discriminator type that defaults to None, maps "0" to None and restores zero padding on legacy ints. Ban.duration also gets its missing None default.
Ban only had a duration in hours, so the tasks service tried to compare that integer against a datetime and never found an expired ban. Ban.expires_at is now set from created_at + duration for temp bans, giving the tasks a field they can query directly.
Older bot versions saved permanent bans with type "perma" while the model default and /bans list filter use "perm", so those bans never matched the filter. Map "perma" to "perm" on validation; the migration rewrites stored documents.
No model declared any index, so every per-message settings lookup and every tasks poll on active/expiry fields was a collection scan. Add compound indexes matching those queries (guild+setting, guild+channel, guild+user, active+expiry, ...); Beanie creates them at startup.
Adds jarvis_core.db.migrations (also installed as the jarvis-migrate command) to run once before deploying 2.0. It converts string-encoded Discord IDs to int64 across every model, including embedded documents and lists, rewrites the legacy "perma" ban type, normalizes discriminators, and backfills Ban.expires_at for temporary bans. Supports --dry-run and is safe to re-run.
Both helpers fetched any URL a user supplied, so the bot could be pointed at internal services (Mongo, Redis, cloud metadata) and used to probe them. Requests now go through public_session()/fetch(): only http(s), IP literals and every redirect hop are checked, and a resolver drops non-public DNS answers at connect time (covering DNS rebinding). Requests also get a 30s timeout.

hash() gains max_size, enforced while streaming instead of trusting Content-Length, and only treats data as a URL when it is a single http(s) URL; text such as "notes.txt hello" previously matched the loose URL regex and failed to hash.

BREAKING CHANGE: hash() no longer fetches scheme-less URLs (they are hashed as text), and both helpers raise UnsafeURLError (a ValueError) for blocked URLs.
Each call attached another RichHandler to the same logger, so calling it more than once for a name duplicated every log line.
get() with no filters indexed sequence[0], which failed for empty lists and non-indexable iterables despite accepting any Iterable; it now returns the first element or None. unconvert_bytesize() rejected units such as "K" with an unhelpful list.index error; it now accepts them as "KB" and raises a clear ValueError for unknown units.
The character classes contained "&amp;" (from a copy-paste) instead of "&", which also admitted the stray characters a, m, p and ;.
Both tests were named test_ipv4, so the second definition replaced the first and the IPv4 regex was never exercised.
pre-commit-hooks 6.0.0, black 26.10.1, isort 9.0.2 and flake8 7.5.0, and pre-commit 4.7.0 itself. isort moves from the archived mirrors-isort repo to PyCQA/isort.
Drop the nonexistent jarvis_core.config module from the README and describe the db, log and http helpers, how to run the tests against a real MongoDB, and how to migrate a 1.x database before deploying 2.0.
Creating a time-series collection adds system.views, which ordinary users cannot drop, so wiping the test database failed once one existed.
ci: move from GitLab CI to Forgejo Actions
Some checks failed
CI / precommit (push) Failing after 1s
CI / test (3.11) (push) Failing after 1s
CI / test (3.12) (push) Failing after 1s
CI / release (push) Has been skipped
e2534e6f6c
Replace the GitLab pipeline with a Forgejo Actions workflow: pre-commit, tests on Python 3.11 and 3.12 against a Mongo service, and uv build and publish to the Forgejo package registry on tags. Needs a PACKAGE_TOKEN secret; the GitLab SAST template has no equivalent and is dropped.
ci: run workflows on ubuntu-latest
Some checks failed
CI / precommit (push) Failing after 2s
CI / test (3.11) (push) Failing after 8s
CI / test (3.12) (push) Failing after 25s
CI / release (push) Has been skipped
a744b79c10
Use the ubuntu-latest runner label instead of docker.
ci: run jobs in the runner image instead of python containers
Some checks failed
CI / precommit (push) Failing after 44s
CI / test (3.12) (push) Successful in 24s
CI / test (3.11) (push) Failing after 1m30s
CI / release (push) Has been skipped
c4e0e14336
The python job containers have no Node, which actions/checkout needs. Drop them, install uv in the ubuntu-latest image and let it fetch Python, and build Docker images on ubuntu-full-latest.
style: format with black 26 and fix the flake8 findings
Some checks failed
CI / precommit (push) Successful in 44s
CI / test (3.12) (push) Successful in 25s
CI / test (3.11) (push) Failing after 1m28s
CI / release (push) Has been skipped
f5ed64111f
CI runs pre-commit over the whole tree. Apply black 26's formatting (blank line after module docstrings and similar) and isort, drop an unused import, and mark the migration CLI's report output as intentional print().
ci: Test against Python 3.12-3.15
Some checks failed
CI / test (3.12) (push) Successful in 2m0s
CI / precommit (push) Successful in 2m30s
CI / test (3.14) (push) Successful in 53s
CI / test (3.13) (push) Failing after 2m21s
CI / test (3.15) (push) Successful in 1m12s
CI / release (push) Has been skipped
767949b89f
ci: raise the open-file limit of the Mongo service container
All checks were successful
CI / test (3.12) (push) Successful in 26s
CI / precommit (push) Successful in 1m26s
CI / test (3.13) (push) Successful in 59s
CI / test (3.14) (push) Successful in 28s
CI / test (3.15) (push) Successful in 30s
CI / release (push) Has been skipped
CI / test (3.12) (pull_request) Successful in 26s
CI / precommit (pull_request) Successful in 46s
CI / test (3.14) (pull_request) Successful in 26s
CI / test (3.13) (pull_request) Successful in 27s
CI / test (3.15) (pull_request) Successful in 28s
CI / release (pull_request) Has been skipped
25960d0a55
On a runner host whose containers default to a 1024 open-file limit, mongod aborts with WiredTiger 'Too many open files' partway through the suite and every later test fails with connection errors. Start the service with --ulimit nofile=64000:64000, the limit MongoDB recommends.
A per-guild rule: when a member reaches exactly N active warnings, mute (with a duration, up to Discord's 28 days), kick or ban them. Validation refuses a mute without a duration, a duration on anything else, an unknown action, and a count below one. Indexed by guild and count.
chore(release): 3.0.0
Some checks failed
CI / test (3.12) (push) Failing after 28s
CI / precommit (push) Successful in 46s
CI / test (3.13) (push) Failing after 30s
CI / test (3.14) (push) Failing after 26s
CI / test (3.15) (push) Failing after 29s
CI / release (push) Has been skipped
CI / precommit (pull_request) Successful in 46s
CI / test (3.12) (pull_request) Failing after 33s
CI / test (3.13) (pull_request) Failing after 29s
CI / test (3.14) (pull_request) Failing after 30s
CI / test (3.15) (pull_request) Failing after 28s
CI / release (pull_request) Has been skipped
213ff22b8f
Move jarvis-core to 3.0.0 so it shares a version with jarvis-bot and jarvis-tasks, and say so in the README: 3.x of each work with 3.x of the others. Also includes the Escalation model added since the 2.0.0 tag.
Tag gains uses (how often it was fetched) and aliases (other names it answers to), both defaulting to empty so existing tags load unchanged, plus an index on guild and alias for lookups.
Pinboard gains an optional threshold (at least 1; None keeps the board manual-only, as every existing board is) and the emoji that is counted, defaulting to a star.
Rolegiver groups gain exclusive (one role at a time) and required_role (a role needed before picking), both off by default. The new RoleMenu records a posted role-picker message (guild, channel, message and group) so the bot can recognise it after a restart and refresh it when the group changes.
Black formatting for the tests added with the tag, pinboard and rolegiver fields, so pre-commit passes in CI.
test_models builds a default instance of every model, and could not build an Escalation: its action is a Literal and its warning count has a lower bound of one. Pick a Literal value that needs no companion fields and honour ge constraints, so the test covers every model again.
style: format the generic model test
All checks were successful
CI / test (3.12) (push) Successful in 29s
CI / precommit (push) Successful in 42s
CI / test (3.13) (push) Successful in 39s
CI / test (3.14) (push) Successful in 32s
CI / test (3.15) (push) Successful in 31s
CI / release (push) Has been skipped
CI / precommit (pull_request) Successful in 44s
CI / test (3.12) (pull_request) Successful in 31s
CI / test (3.13) (pull_request) Successful in 30s
CI / test (3.14) (pull_request) Successful in 31s
CI / test (3.15) (pull_request) Successful in 29s
CI / release (pull_request) Has been skipped
eb6b462ebc
Black formatting for the Literal and bound handling added to test_models, so pre-commit passes in CI.
docs: describe the website, its cookies and sessions in the privacy policy and terms
All checks were successful
CI / test (3.12) (push) Successful in 32s
CI / precommit (push) Successful in 43s
CI / test (3.13) (push) Successful in 34s
CI / test (3.14) (push) Successful in 31s
CI / test (3.15) (push) Successful in 34s
CI / release (push) Has been skipped
CI / precommit (pull_request) Successful in 45s
CI / test (3.12) (pull_request) Successful in 32s
CI / test (3.13) (pull_request) Successful in 33s
CI / test (3.14) (pull_request) Successful in 33s
CI / test (3.15) (pull_request) Successful in 31s
CI / release (pull_request) Has been skipped
da8bcf0442
feat(models): let web requests refresh a rolegiver group's posted menus
All checks were successful
CI / test (3.12) (push) Successful in 43s
CI / precommit (push) Successful in 49s
CI / test (3.14) (push) Successful in 37s
CI / test (3.13) (push) Successful in 42s
CI / test (3.15) (push) Successful in 35s
CI / release (push) Has been skipped
CI / precommit (pull_request) Successful in 45s
CI / test (3.12) (pull_request) Successful in 40s
CI / test (3.13) (pull_request) Successful in 46s
CI / test (3.14) (pull_request) Successful in 37s
CI / test (3.15) (pull_request) Successful in 41s
CI / release (pull_request) Has been skipped
85f0acd12e
docs: describe retention and appeal notices in the privacy policy
All checks were successful
CI / test (3.12) (pull_request) Successful in 43s
CI / precommit (pull_request) Successful in 1m8s
CI / test (3.14) (pull_request) Successful in 38s
CI / test (3.13) (pull_request) Successful in 44s
CI / test (3.15) (pull_request) Successful in 38s
CI / release (pull_request) Has been skipped
CI / precommit (push) Successful in 41s
CI / test (3.12) (push) Successful in 36s
CI / test (3.13) (push) Successful in 37s
CI / test (3.14) (push) Successful in 35s
CI / test (3.15) (push) Successful in 35s
CI / release (push) Successful in 4s
c8932ab813
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jarvis/jarvis-core!1
No description provided.