Conversation
Public/unauthenticated mode (closes #32) - Api gains keyword-only require_auth=True parameter; default preserves strict behavior so no existing caller changes. - __check_login early-returns when require_auth=False and no token is present, instead of raising LoginErrorException. - _get and _get_paginated omit the Authorization header when auth_id is None. Sending no header beats sending "Bearer None". - CLI: --no-auth global flag on the group, Api constructed once via ctx.obj["api"]; all 13 commands take click.Context via pass_context. Verified end-to-end: `truthbrush --no-auth user realDonaldTrump` returns JSON; `truthbrush trends` (strict, no creds) still raises. Python 3.14 - pyproject.toml: python = "^3.14"; ruff target-version = "py314". - Workflow: actions/setup-python bumped to @v5 with python-version: 3.14 (3.10-dev was a stale pre-release pin). - Ruff auto-fix pass: timezone.utc -> UTC; stale quoted forward-ref on date_to_bound unquoted. Type hygiene - ty check truthbrush/ goes from 35 pre-existing diagnostics to zero: x: T = None defaults -> X | None = None throughout; generator return types -> Iterator[T]; datetime.utcnow() -> datetime.now(UTC); ratelimit_reset null-guarded; sorted() over JSON payload narrowed via cast(list[dict], result); pull_statuses result-shape validation reordered so a non-list response breaks the loop instead of crashing in sorted(). Drive-bys cherry-picked from PR #41 - search(resolve: bool = 4) -> True. Default was an int despite the bool annotation. - pull_statuses(username=None, ..., *, user_id=None): either identifier works; supplying user_id directly skips the lookup roundtrip, which matters in public mode where lookup may be gated. Also calls __check_login explicitly (previously relied on transitive call). Tests - Two offline tests: one asserts Api(require_auth=False) without creds does not raise on a method call that short-circuits before HTTP; one asserts the default strict mode still raises LoginErrorException without creds. Both use monkeypatch.delenv to guard against a loaded .env. README - New "Public mode (no credentials)" subsection under Installation. - --no-auth added to the help block. - Stale "Please format your code with black" replaced with the ruff/ty workflow introduced in 0.3.0. Authors - pyproject.toml authors gains David Thiel.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Api(require_auth=False)ortruthbrush --no-auth …. Default stays strict, so existing callers see no change.setup-python@v5+python-version: "3.14".ty check truthbrush/from 35 pre-existing diagnostics to zero; ruff format + ruff check are clean.search(resolve: bool = 4)default (was an int), and letspull_statusesacceptuser_iddirectly so public-mode callers can skip an extra lookup.Public mode — how it works
Api.__init__grows a keyword-onlyrequire_auth: bool = Trueflag. WhenFalseand no token is set,__check_loginearly-returns instead of raisingLoginErrorException._get/_get_paginatedbuild a headers dict that only attachesAuthorization: Bearer …whenself.auth_id is not None— no more accidentalBearer None.CLI: module-level
api = Api()is gone. Thecligroup takes--no-auth, constructsApi(require_auth=not no_auth), and stashes it inctx.obj["api"]. All 13 commands pick up@click.pass_context+ actx: click.Contextparameter.Verified end-to-end with an empty environment:
$ env -u TRUTHSOCIAL_TOKEN -u TRUTHSOCIAL_USERNAME -u TRUTHSOCIAL_PASSWORD \ truthbrush --no-auth user realDonaldTrump {"id": "107780257626128497", "username": "realDonaldTrump", …} $ env -u TRUTHSOCIAL_TOKEN -u TRUTHSOCIAL_USERNAME -u TRUTHSOCIAL_PASSWORD \ truthbrush --no-auth trends {"errors": [{"error_code": "USER_UNAUTHENTICATED", …}], …} $ env -u TRUTHSOCIAL_TOKEN -u TRUTHSOCIAL_USERNAME -u TRUTHSOCIAL_PASSWORD \ truthbrush trends # no --no-auth truthbrush.api.LoginErrorException: Username is missing.So the client doesn't try to decide which endpoints are public — it just passes through unauthenticated and lets Truth Social's server return whatever it returns.
Cherry-picks from PR #41
search(resolve: bool = 4)→resolve: bool = True. The default was an int despite the bool annotation — a latent bug.pull_statuses(username=None, *, user_id=None): either identifier works; supplyinguser_idskips theself.lookup(username)roundtrip. Relevant to public mode sincelookupmay or may not be publicly accessible. Also calls__check_loginexplicitly now (previously it relied on the transitive call throughlookup).Not cherry-picked: the broader test/CI restructure, the
utils.pysplit, and the__check_login→_check_loginrename — all scope creep for this PR.Tests
Two new offline tests in
test/test_api.py:test_public_mode_does_not_require_credentials— constructsApi(require_auth=False)with no creds and callsuser_likes("abc", top_num=0), which short-circuits after__check_loginreturns. Old code would have raisedLoginErrorExceptionfrom__check_login; new code returns gracefully.test_strict_mode_still_raises_without_credentials— sameApi()in strict mode,lookup(…)must raise.Both use
monkeypatch.delenvas belt-and-suspenders against a loaded.env.