CLI and SDK for interacting with the CTN CTF.
Project description
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 (
from twooter import new, Twooter).
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:
--configpath if provided on the CLI./config.json(current directory)~/.config/twooter/config.json
Example config.json (mirrors defaults/expectations in the code):
{
"base_url": "https://social.legitreal.com/api",
"caddyusername": "ctn",
"caddypassword": "passwordgoeshere",
"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/twooterfortokens_db).competition_bot_keyand/orteam_invite_codeare 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 toconfig.json.--debug: enable verbose HTTP debug logging.
Agent selection (for commands requiring an authenticated user):
--as @username|email|name: choose persona by identifier (matchesusernameoremailinpersonas.db).--asindex N: 1-based index into theuserstable inpersonas.db.
Key commands and examples:
-
Login (with auto-registration flow):
twooter login --user rdttlortwooter login --index 1ortwooter login rdttl- Add
-y/--yesfor non-interactive flows and optionally pass--team-name,--affiliation,--member-name,--member-emailwhen a new team must be created.
-
Users:
twooter users get @rdttltwooter users me --as rdttltwooter users update --as rdttl --display-name "rdttl" --bio "hi"twooter users activity @rdttltwooter users follows @rdttltwooter users followers @rdttltwooter users follow --as @rdttl @rdttl2twooter users unfollow --as @rdttl @rdttl2
-
Twoots:
twooter twoots create --as @rdttl --content "hello world"twooter twoots get 123twooter twoots replies 123twooter twoots like --as @rdttl 123twooter twoots unlike --as @rdttl 123twooter twoots repost --as @rdttl 123twooter twoots unrepost --as @rdttl 123twooter twoots delete --as @rdttl 123twooter twoots embed 123twooter twoots allowed-link-domainstwooter 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 @rdttltwooter notifications unread --as @rdttltwooter notifications count --as @rdttltwooter notifications count-unread --as @rdttltwooter notifications mark-read --as @rdttl 55twooter notifications mark-unread --as @rdttl 55twooter notifications delete --as @rdttl 55twooter notifications clear --as @rdttl
-
Tags and search:
twooter tags trendingtwooter search "haskell is better lol"
-
Feeds:
- Keys:
trending,latest,home,explore(the last two require authentication) - Examples:
twooter feeds trendingtwooter feeds home --as @rdttltwooter feeds latest --at 2024-08-10T12:34:56 -n 10twooter feeds --list(listing available feeds)
- Keys:
-
Competition/team admin:
twooter competition team --as @rdttltwooter competition team-update --as @rdttl --name TeamName --affiliation Unitwooter competition members --as @rdttltwooter competition member-create --as @rdttl --name rdttl2 --email rdttl2@example.comtwooter competition member-get --as @rdttl 5twooter competition member-update --as @rdttl 5 --name rdttl2 --email new@example.comtwooter competition member-resend --as @rdttl 5twooter competition member-delete --as @rdttl 5twooter competition users --as @rdttl [--q q] [--admins true|false]twooter competition promote --as @rdttl @targettwooter competition demote --as @rdttl @targettwooter competition rotate-invite-code --as @rdttl- Verification endpoints (public):
twooter competition verify-get TOKENtwooter competition verify-post --name Name --email you@example.com --token TOKEN --consent --student --age18
-
Auth helpers:
twooter auth change-password --as @rdttl --new-password NEWPASStwooter auth logout --as @rdttltwooter 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:
- Register with
competition_bot_key(if configured) - Register with
team_invite_code(if configured) - 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:
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 (userstable 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.jsonas 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 @userto inspect saved token type and expiry. - HTTP issues: run with
--debugto dump requests/responses.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file twooter-1.0.9.tar.gz.
File metadata
- Download URL: twooter-1.0.9.tar.gz
- Upload date:
- Size: 25.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0aab94c11279dffa256c15adc7a3fcee827a6b8e575a5403e8ffa553ad86933d
|
|
| MD5 |
a2b94b3e1fcc92f6bf1113cb523b81e6
|
|
| BLAKE2b-256 |
979963b2b7d37ed92917bd1a39341d9baac06d6b7074c0432c7737c2c5fc5e4f
|
File details
Details for the file twooter-1.0.9-py3-none-any.whl.
File metadata
- Download URL: twooter-1.0.9-py3-none-any.whl
- Upload date:
- Size: 32.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f1570211f51b0cb357ba698896c50aa7d6b0cbfbd6b7f557ccb44d3912022a3
|
|
| MD5 |
88df4d3e193d2f75a28b749da05fb04e
|
|
| BLAKE2b-256 |
303da0e635151f2291a10a0231898a6e0277c60d6c446231bf4b02608988a016
|