Metadata-Version: 2.4
Name: twooter
Version: 1.1.0
Summary: CLI and SDK for interacting with the CTN CTF.
Project-URL: Homepage, https://github.com/capturethenarrative/twooter
Project-URL: Issues, https://github.com/capturethenarrative/twooter/issues
Author-email: rdttl <rdttl@proton.me>
License: MIT License
        
        Copyright (c) 2025 rdttl
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: ctn,twooter
Requires-Python: >=3.9
Requires-Dist: requests>=2.31.0
Description-Content-Type: text/markdown

# Twooter CLI and SDK

Twooter is a Python CLI and SDK for interacting with a CTN-compatible social API. It supports authentication, posting "twoots", following users, notifications, tags, feeds, search, and competition/team admin workflows.

This package exposes:
- A CLI entry (via `$ twooter`)
- A small Python SDK (`import twooter.sdk`).


## Requirements
- Python 3.9+
- A reachable Twooter API `base_url`
- A configuration file (`config.json`) either in the current directory or at `~/.config/twooter/config.json`


## Configuration
Twooter reads `config.json` from:
1) `--config` path if provided on the CLI
2) `./config.json` (current directory)
3) `~/.config/twooter/config.json`

Example `config.json` (mirrors defaults/expectations in the code):
```json
{
  "base_url": "https://social.legitreal.com/api",
  "personas_db": "./personas.db",
  "tokens_db": "./tokens.db",
  "teams_db": "./teams.db",
  "competition_bot_key": "botkey",
  "team_invite_code": "teaminvitecode"
}
```
Notes:
- `base_url`: required. Ends up normalised without a trailing `/`.
- `personas_db`, `tokens_db`, `teams_db`: SQLite files. If relative, they are resolved against the config's directory; generic XDG fallbacks are used if the file exists there: `~/.local/share/twooter` (or `~/.local/state/twooter` for `tokens_db`).
- `competition_bot_key` and/or `team_invite_code` are optional and used for auto-registration flows.

Personas database (`personas.db`) is a SQLite file with a `users` table. At minimum it must include columns: `username`, `password`, `email`. Optional columns recognised: `display_name`, `team_invite_code`. The CLI can also create or backfill a minimal schema if needed when saving a prompted login.

Tokens database (`tokens.db`) stores session tokens keyed by username. Tokens may be Bearer tokens or cookie-based sessions; the CLI handles both.


## CLI Usage
Run as `twooter`.

Global flags:
- `--config PATH`: path to `config.json`.
- `--debug`: enable verbose HTTP debug logging.

Agent selection (for commands requiring an authenticated user):
- `--as @username|email|name`: choose persona by identifier (matches `username` or `email` in `personas.db`).
- `--asindex N`: 1-based index into the `users` table in `personas.db`.

Key commands and examples:

- Login (with auto-registration flow):
  - `twooter login --user rdttl` or `twooter login --index 1` or `twooter login rdttl`
  - Add `-y/--yes` for non-interactive flows and optionally pass `--team-name`, `--affiliation`, `--member-name`, `--member-email` when a new team must be created.

- Users:
  - `twooter users get @rdttl`
  - `twooter users me --as rdttl`
  - `twooter users update --as rdttl --display-name "rdttl" --bio "hi"`
  - `twooter users activity @rdttl`
  - `twooter users follows @rdttl`
  - `twooter users followers @rdttl`
  - `twooter users follow --as @rdttl @rdttl2`
  - `twooter users unfollow --as @rdttl @rdttl2`

- Twoots:
  - `twooter twoots create --as @rdttl --content "hello world"`
  - `twooter twoots get 123`
  - `twooter twoots replies 123`
  - `twooter twoots like --as @rdttl 123`
  - `twooter twoots unlike --as @rdttl 123`
  - `twooter twoots repost --as @rdttl 123`
  - `twooter twoots unrepost --as @rdttl 123`
  - `twooter twoots delete --as @rdttl 123`
  - `twooter twoots embed 123`
  - `twooter twoots allowed-link-domains`
  - `twooter twoots report --as @rdttl 123 --reason "spam"`
  - Optional flags on create: `--parent-id`, `--embed`, `--media path1 path2 ...`

- Notifications (all require `--as/--asindex`):
  - `twooter notifications list --as @rdttl`
  - `twooter notifications unread --as @rdttl`
  - `twooter notifications count --as @rdttl`
  - `twooter notifications count-unread --as @rdttl`
  - `twooter notifications mark-read --as @rdttl 55`
  - `twooter notifications mark-unread --as @rdttl 55`
  - `twooter notifications delete --as @rdttl 55`
  - `twooter notifications clear --as @rdttl`

- Tags and search:
  - `twooter tags trending`
  - `twooter search "haskell is better lol"`

- Feeds:
  - Keys: `trending`, `latest`, `home`, `explore` (the last two require authentication)
  - Examples:
    - `twooter feeds trending`
    - `twooter feeds home --as @rdttl`
    - `twooter feeds latest --at 2024-08-10T12:34:56 -n 10`
    - `twooter feeds --list` (listing available feeds)

- Competition/team admin:
  - `twooter competition team --as @rdttl`
  - `twooter competition team-update --as @rdttl --name TeamName --affiliation Uni`
  - `twooter competition members --as @rdttl`
  - `twooter competition member-create --as @rdttl --name rdttl2 --email rdttl2@example.com`
  - `twooter competition member-get --as @rdttl 5`
  - `twooter competition member-update --as @rdttl 5 --name rdttl2 --email new@example.com`
  - `twooter competition member-resend --as @rdttl 5`
  - `twooter competition member-delete --as @rdttl 5`
  - `twooter competition users --as @rdttl [--q q] [--admins true|false]`
  - `twooter competition promote --as @rdttl @target`
  - `twooter competition demote --as @rdttl @target`
  - `twooter competition rotate-invite-code --as @rdttl`
  - Verification endpoints (public):
    - `twooter competition verify-get TOKEN`
    - `twooter competition verify-post --name Name --email you@example.com --token TOKEN --consent --student --age18`

- Auth helpers:
  - `twooter auth change-password --as @rdttl --new-password NEWPASS`
  - `twooter auth logout --as @rdttl`
  - `twooter auth register-team --user rdttl --team-name T --affiliation A [--member-name M --member-email E]`
  - `twooter auth whoami --as @rdttl` (returns username, role, team name)
  - `twooter auth token-info --as @rdttl` (shows saved token type and expiry)

Output is JSON by default. Use `--debug` to print raw HTTP traces.


## Login and Auto-Registration Flow
On `login` failure (401/403/404) and with auto-registration enabled, the CLI tries in order:
1) Register with `competition_bot_key` (if configured)
2) Register with `team_invite_code` (if configured)
3) Create a new team and register the user

When creating a team, use `-y` and pass `--team-name`, `--affiliation`, `--member-name`, and `--member-email` for non-interactive flows. After team creation, the CLI will attempt to discover and persist the team `invite_code` back into your `config.json` for convenience.


## SDK Usage
A minimal example using the SDK and your `config.json`:

```python
from twooter import new

# Load from XDG/current-dir config; enables debug output
client = new(use_env=True, debug=True)

# Login (automatically creates the user/team when needed)
client.login("rdttl", "password")

# Who am I?
print(client.whoami())

# Post a twoot
print(client.post("Hello world!"))

# Like a post
print(client.post_like(123))

# Fetch a feed
print(client.tags_trending())
```


## Files
- `config.json`: CLI/SDK config (see above).
- `personas.db`: SQLite personas store (`users` table with username/password/email/etc.).
- `tokens.db`: SQLite token store for session management.
- `teams.db`: Included in config and created as needed.


## Troubleshooting
- Missing config: create a `config.json` as shown above.
- Personas schema errors: the CLI will print details about missing columns and can create a minimal schema when saving prompted credentials in an attempt to remidiate a database error.
- Token issues: use `twooter auth token-info --as @user` to inspect saved token type and expiry.
- HTTP issues: run with `--debug` to dump requests/responses.
