Nookara for Python
pip install nookara
pip install "nookara[realtime]" # websockets
pip install "nookara[signing]" # cryptography, for signed messages and encrypted DMs
from nookara import Client, login, scope_mask
import velora_api keeps working for older code, and the examples below still use that name.
What's in it: sign-in, profiles, friends, DMs, group chats, notifications, storage, credits, Ember chat and bots, bot payments and carts, Products, cart and direct pay, claims and reports, Nookara Drive, Forums, Devices and Apps, as one named method per route.
Who can call what. Some routes only work for a website sign-in or one of Nookara's own apps, and the server refuses anything else. Each method's entry in catalogue.json has a surface:
| surface | Who can call it |
|---|---|
| (none) | Follows the route's own auth and scopes, the normal case for your App |
any |
Any signed-in token: add_to_cart, product_access |
app |
An App with Transfer Credits: quote_directly, pay_directly. Up to 100 credits a day per App per person |
website |
Website sign-in only: your Products, sales, purchases, reading the cart, paying at checkout, reports and claims, managing Apps. Listed so the reference is complete, your App can't call them |
drive |
Nookara Drive's own app only, or Nookara for Windows |
forums |
Nookara Forums' own app only, for anything that changes something. Reading needs no token |
devices |
Nookara for Windows or a website sign-in |
To charge someone from your App, send them to checkout with add_to_cart, or use pay_directly if they gave you Transfer Credits.
About this client
Nookara is the identity and platform API: accounts, direct messages, groups, friends,
credits, instance tickets, signing keys and hosting. Bonfire is the chat system. Every
Bonfire server is an instance at its own web address (the main community is
https://chat.ikelene.net; Nookara-hosted ones are https://<slug>-bonfire.ikelene.net;
you can also self-host). One instance holds one community. This package talks to both.
It supports Python 3.10+ and exposes every documented REST operation as a named
method, plus a generic request method. REST needs only the Python standard
library. The optional realtime extra installs websockets; the optional
signing extra installs cryptography for message signing and verification.
Install
Use the commands at the top of this page. From a source checkout, run python -m pip install ".[realtime]"
inside the folder containing pyproject.toml. requirements.txt installs the
optional websocket dependency only, not the Nookara package itself.
Sign in, and what scopes mean now
from velora_api import Client, login, scope_mask
token = login(project_name="My Python client", scopes=scope_mask("PROFILE_READ", "CHAT"))
velora = Client(token)
print(velora.scope_names()) # what the person actually allowed
print(velora.bonfire_instances()) # the Bonfire servers saved on this account
The browser shows a consent screen. Scopes are requested, never required: the person can untick any of them (the risky ones start unticked), so a token can come back with fewer scopes than you asked for. Ask only for what you use, then check:
velora.has_scope("MEDIA_UPLOAD") # True / False (names, labels such as "Upload Media", or bits)
velora.granted_scopes() # the usable mask (approved and not switched off by an admin)
velora.require_scope("CHAT") # raises ScopeError with a clear message
A call without the scope raises ScopeError (a subclass of APIError) with scope,
label and disabled_by_admin, so you can show "log in again to use this" instead of
crashing. login() never asks for a password. Project names that start with
Bonfire ( are reserved for the official apps. optional_scopes= is still accepted
and merged into scopes. To print the URL yourself use login(..., on_url=print).
Do not print the returned token. For an existing token use Client(os.environ["VELORA_TOKEN"]).
| Scope | Bit | Default on the consent screen | Bonfire instance scope |
|---|---|---|---|
| View Basic Profile | 1 | on | |
| View Sensitive Info | 2 | off | |
| Project Auth | 4 | on | |
| Modify Settings | 8 | off | |
| Read Storage / Write Storage | 16 / 32 | on / off | |
| Read Instant Messages / Send Instant Messages | 64 / 128 | on | |
| View Friends / Manage Friends | 256 / 512 | on / off | |
| Spend Credits | 1024 | off | |
| Chat Messaging | 2048 | on | chat (needed to sign in to an instance) |
| Upload Media | 4096 | on | media (uploads) |
| View Subscriptions | 8192 | on | |
| Nookara Voice | 16384 | on | voice |
| Sign Messages | 32768 | off | sign (signed messages) |
velora_api.SCOPES, SCOPE_LABELS, DEFAULT_OFF, INSTANCE_SCOPES,
scope_mask(), scope_names() and has_scope() give you the same table in code.
Bonfire servers (instances)
from velora_api import Client, Instance
velora = Client(token) # needs Chat (2048)
chat = Instance("https://chat.ikelene.net")
info = chat.discover() # /.well-known/bonfire, no login
print(info["name"], info["accounts_mode"], info["build"]["version"], info["read_only"])
chat.login_with_velora(velora, sign="auto") # Nookara-login instances
# chat.login("name", "password") # local-account instances
# chat.register("name", "password")
print(chat.scopes) # instance scopes of this session: chat, voice, media, sign
channel = chat.channel_id("#general")
for message in chat.messages(channel, limit=20)["messages"]:
print(message["author"]["username"], message["body"])
chat.send_message(channel, "Hello from Python!")
login_with_velora asks Nookara for a short ticket made for that one instance and
trades it at POST /api/v1/auth/velora. Your Nookara token never leaves Nookara. The
ticket carries the instance scopes the person allowed (chat, voice, media, sign);
sign="auto" signs only when Sign Messages was allowed, sign=True raises ScopeError
if not. The instance session lasts 30 days from last use.
Discovery helpers: accounts_mode, features, build (version, commit, build_id),
read_only, instance_info() (GET /api/v1/instance/info, admins also get runtime, stats
and update) and update_available(). Client.server_latest() returns the official
build instances compare with.
Useful methods: servers() / joined_communities(), channels(), members(),
send_message(), edit_message(), delete_message(), react(), pin(), search(),
join(), invites(), create_invite(expires_in=, max_uses=), delete_invite(),
join_invite(code), invite_info(code), join_requests(), accept_join_request(uid),
create_channel(), set_channel_permissions(), kick(), webhooks(),
create_webhook(), bots(), create_bot(), backups(), create_backup(), settings(),
identities(ids), set_notify(). Every route also has a named method
(get_api_v1_servers_by_sid) and instance.call(...). IDs are strings; users look like
v:123, l:7, b:3.
Your servers as one object
from velora_api import Bonfire
bonfire = Bonfire(velora, sign="auto")
bonfire.join_default() # the official server, once per account
for item in bonfire.communities(): # one entry per saved server
print(item["instance"], (item["community"] or {}).get("name"), item["error"])
bonfire.add("https://camp-bonfire.ikelene.net") # saved on the account, follows the person between devices
A server that is down, banned, in local-account mode, or where Chat was declined is
reported in error (unreachable, local_accounts, chat_scope_missing, an API code);
it never stops the others.
Uploads and media import
uploaded = chat.upload("picture.png", cid=channel) # needs the media scope
saved = chat.import_media("https://example.com/cat.png", cid=channel) # the instance fetches an https URL
chat.send_message(channel, "A picture", attachments=[uploaded, saved])
A session without the media scope raises ScopeError before anything is sent.
Uploads also need the community's allow_uploads and the channel's Attach permission.
Signing and verifying
chat.login_with_velora(velora, sign=True) # account key from Nookara, kept in memory
sent = chat.send_message(channel, "Signed")
ok, reason = chat.verify_message(sent["message"]) # you check it, not the instance's `verified` flag
Signing is optional on every velora-mode instance and never required. It needs the
Sign Messages scope and the signing extra. Signatures follow protocol section 8, including
url: entries for link-only GIFs. verify_message reads the public key from Nookara and
checks owner, revocation time and the Ed25519 signature.
Webhooks need no login
from velora_api import execute_webhook
execute_webhook("https://chat.ikelene.net/api/v1/webhooks/ID/TOKEN", "Build finished")
Bots
An instance bot is created on one instance (chat.create_bot("pingbot") returns the
token once) and uses a bot token. REST is Discord-style at /bot/v1 with numeric ids, the
bot gateway is /bot/gateway:
bot = Instance("https://chat.ikelene.net", bot_token=os.environ["BONFIRE_BOT_TOKEN"])
async with bot.bot_gateway() as gateway:
async for event in gateway: # {"type": "MESSAGE_CREATE", "data": {...}}
...
bot.bot_send("NUMERIC_CHANNEL_ID", "pong")
A Nookara developer bot lives on your Nookara account and can be listed in the bot gallery (Discovery > Bots) so others add it to their servers. Each instance makes its own bot account for it, but the token is the same. A bot program serves all of them:
from velora_api import BotProgram
async with BotProgram(os.environ["VELORA_BOT_TOKEN"]) as bot: # GET /bonfire/bots/self/installs
async for event in bot: # {"instance", "type", "data", "seq"}
if event["type"] == "MESSAGE_CREATE" and event["data"]["content"] == "!ping":
await bot.send(event["instance"], event["data"]["channel_id"], "pong")
It opens one gateway per install, re-reads the install list every 5 minutes, and keeps
going when one instance fails. Gallery owner calls: list_gallery(), save_gallery_listing(),
submit_to_gallery(), gallery_status(), withdraw_from_gallery(), gallery_ticket() and
Instance.add_gallery_bot(ticket).
User gateway
chat.login_with_velora(velora)
async with chat.gateway() as gateway:
async for event in gateway:
if event["type"] == "chat_message":
print(event["message"]["body"])
# or: @gateway.on("chat_message") ... then: await gateway.run()
await_ack = await gateway.send_message(channel, "hi") # waits for chat_ack
It reconnects with capped backoff and jitter (1s to 30s), pings every 25 s, and stops on
close codes 4001 and 4003. There is no replay: after gateway_ready follows a drop,
refetch history over REST and dedupe by id.
Other Nookara-side Bonfire helpers
Client: instance_token(url), signing_key(), bonfire_instances(),
set_bonfire_instances(urls), sync_bonfire_instances(urls), bonfire_default_instance(),
bonfire_default_joined(), bonfire_instance_prefs(), bonfire_directory(q),
bonfire_profiles(ids), bonfire_key(kid), bonfire_jwks(), bonfire_scopes(),
bonfire_import_platforms() / start_bonfire_import() (Discord and Fluxer bundles),
and hosting: hosting_plans(), hosting_servers(),
create_hosting_server(slug, name, plan, weeks=), buy_hosting_weeks(slug, weeks),
backup_hosting_server(slug), hosting_backups(), upload_hosting_backup(...),
delete_hosting_server(slug). Plus is prepaid in weeks and never renews by itself; it is
free for Nookara Pro. Examples are in examples/bonfire_*.py.
Nookara REST methods
Nookara-hosted chat servers (/chat/servers, /chat/channels and so on) are
legacy and read-only now (writes answer server_read_only with moved_to). Direct
messages, groups, friends, storage, credits and presence are unchanged.
velora.send_dm("someone", "Hello from Python!")
velora.set_activity("My game", state="In the menu", ttl=600)
velora.clear_activity()
The old servers(), server(), messages() and send_message() helpers on Client
still call the legacy routes (they now emit a DeprecationWarning); use Instance for
new work.
Group chats have reactions, edits and pins, and DMs have pins (0.6.3):
velora.patch_groups_by_room_messages_by_id(room="grp_x", id="a1b2c3", json={"message": "fixed"}) # your own message
velora.delete_groups_by_room_messages_by_id(room="grp_x", id="a1b2c3") # your own message
velora.put_groups_by_room_messages_by_id_reactions_by_emoji(room="grp_x", id="a1b2c3", emoji="\N{THUMBS UP SIGN}")
velora.delete_groups_by_room_messages_by_id_reactions_by_emoji(room="grp_x", id="a1b2c3", emoji="\N{THUMBS UP SIGN}")
velora.put_groups_by_room_messages_by_id_pin(room="grp_x", id="a1b2c3") # any member, 50 per group
velora.get_groups_by_room_pins(room="grp_x")["messages"]
velora.put_im_by_id_pin(id=123) # either person, 50 per conversation
velora.get_im_with_by_username_pins(username="someone")["messages"]
The realtime client gets room_message_edited, room_message_reactions, room_message_deleted, room_pins_update,
im_deleted and im_pins_update.
send_dm(username, message) requires Send Instant Messages (128). Storage uses Read (16) /
Write (32); its values are strings. Use json.dumps() / json.loads() for structured data.
Every operation in the downloadable catalogues (Nookara on Client, instances on Instance)
has a named method. For example:
chat.post_api_v1_servers_by_sid_roles(
sid="YOUR_SERVER_SID", json={"name": "Member", "permissions": 257}
)
chat.call("get_api_v1_servers_by_sid", sid="YOUR_SERVER_SID")
velora.call("get_bonfire_hosting_plans")
velora.request("GET", "/bonfire/instances")
Use query={...} for queries, json={...} for JSON, or data={...}, files={...} for
multipart. Path parameters are URL encoded; optional parameters may be omitted. The API
checks token scopes, server/channel roles, rank and privacy separately. File values can
also be (filename, bytes_or_file_object, mime_type). This client buffers multipart data
with a 64 MiB limit; server limits can be smaller.
Errors
from velora_api import APIError, ScopeError, TransportError
try:
chat.upload("picture.png")
except ScopeError as exc:
print("Log in again and keep this permission ticked:", exc.label or exc.error)
except APIError as exc:
print(exc.status, exc.error, exc.retry_after, exc.moved_to)
except TransportError:
print("Network error. Check whether a write completed before retrying.")
The library does not retry requests automatically. Handle 429 using Retry-After or
retry_after when present, with backoff. Error payloads are available as exc.payload.
Some endpoints return empty 204, text or binary responses; use raw=True for bytes.
Redirects are rejected so credentials are never forwarded.
Realtime user client (Nookara)
import asyncio
import os
from velora_api import RealtimeClient
async def main():
async with RealtimeClient(os.environ["VELORA_TOKEN"]) as socket:
await socket.set_activity("My Python app", state="Connected")
async for event in socket:
if event.get("type") in ("im_received", "presence_update"):
print(event)
asyncio.run(main())
This is Nookara's own socket: presence, direct messages, groups, calls. Entering the context
waits for auth_success, checks tos_required, starts the reader and sends chat_hello
when the Chat scope is granted. Its chat_* frames only concern legacy Nookara-hosted
servers; Bonfire chat uses the instance gateway above. rpc() is only for frames with nonce
replies. On disconnect, reconnect, authenticate, refetch REST state and rebuild presence.
There is no durable websocket replay.
This package transports voice signaling but does not implement WebRTC, audio capture/playback or a desktop UI. See the voice guide for a WebRTC implementation, including two peer connections and subscription snapshots.
Legacy Nookara bots and webhooks
These Nookara-hosted routes are read-only now; use the instance bots and webhooks above.
bot = Client(os.environ["VELORA_BOT_TOKEN"], auth_scheme="Bot")
me = bot.get_chat_api_v1_users_me()
Game servers (Bonfire Link, Minecraft)
On a Bonfire instance the game plugin connects out to the instance
(bonfire server https://chat.example in the game console); an admin picks it from the
connected list:
for server in chat.game_connected()["instances"]: # waiting to be linked (Manage server)
print(server["iid"], server["pairing_tag"], server["motd"])
chat.game_link(iid, cid=GAME_CHANNEL_CID)
print(chat.game_status(GAME_CHANNEL_CID))
Client.game_* still exist for the legacy Nookara routes. Console commands may require the
website or the Bonfire app (console_needs_bonfire).
Bot payments (0.5.0)
A bot can ask a Nookara user for credits. The bot authenticates with its bot token; the buyer
opens the returned pay_url, signs in with their normal login (no scopes, no app authorization)
and presses Pay. The credits go to the bot's owner. There is no webhook, so you poll.
from velora_api import BotClient, is_paid, OrderConflict, BuyerError, PaymentError
bot = BotClient(os.environ["BOT_TOKEN"])
request = bot.create_payment_request("alice", 20, "ord-1001", description="Premium pack",
return_url="https://shop.example/done", expires_in=900)
print(request["pay_url"]) # send the buyer here, do not log it publicly
final = bot.wait_for_payment(request["id"], timeout=900, poll=3) # PaymentTimeout when still pending
if is_paid(final) and final["amount"] == 20: # fulfil ONLY when paid; check amount and buyer id
...
get_payment_request(id), find_payment_request(order_id) (None if unknown),
list_payment_requests(status=None, limit=None) and cancel_payment_request(id) complete the set.
Repeating create_payment_request with the same order id, buyer and amount returns the existing request
(idempotent_replay is true); a different buyer or amount raises OrderConflict. Errors are
PaymentError subclasses (still APIError): InvalidBotToken, MerchantUnavailable,
InvalidPaymentRequest, BuyerError, OrderConflict, PaymentInProgress, NotCancellable,
PaymentNotFound and PaymentRateLimited; error.error keeps the server code. Run this on a server:
never put a bot token in a Scratch/PenguinMod project, web page or app (the PenguinMod extension has no
payment blocks for that reason). See examples/bot_payments.py and the Bot payments section of the guide.
Bot presence: a bot is online in a community while it holds an authenticated /bot/gateway connection.
Member objects from Instance.members(...) carry presence ("online" or "offline" for bots, null for
people) and the instance websocket (InstanceGateway) delivers chat_presence events; the library passes these through unchanged.
Client.bot_installs() includes staff test installs with test: true (not counted as real installs; skip them).
Notifications (0.5.7)
Friend requests, credits, store sales, badges, membership, hosting and messages from the Nookara team are notifications (the bell on the site and in Ember). System DMs are gone: those messages are notifications now.
page = velora.notifications(unread=True, limit=20) # {"notifications": [...], "next_cursor": ..., "unread": n}
for n in page["notifications"]:
print(n["category"], n["title"], n["body"]) # plain text, **bold** is the only markup
velora.mark_notifications_read([n["id"] for n in page["notifications"]]) # or mark_notifications_read(all=True)
print(velora.unread_notifications())
for category in velora.notification_settings(): # locked ones (system, moderation) are always on
print(category["key"], category["list"], category["email"], category["locked"])
velora.set_notification_settings({"friends": {"email": False}})
mark_notifications_unread(ids) and delete_notification(id) complete the set; the generated methods are
get_account_notifications, get_account_notifications_unread_count, post_account_notifications_read,
post_account_notifications_unread, delete_account_notifications_by_id, get_account_notification_settings
and patch_account_notification_settings. Live changes arrive on the user websocket:
async with RealtimeClient(os.environ["VELORA_TOKEN"]) as socket:
async for event in socket.notification_events(): # notification_new / notifications_updated
print(event["type"], event["unread"])
notification_new carries notification (same shape as the list) and unread; notifications_updated carries
unread and one of read (ids or "all"), unread_ids or deleted. The older /notifications routes still
exist for the Scratch extension. Staff can send with post_admin_notifications_send.
Bot aliases (0.5.8)
A bot can post a message under another name and avatar. The message still belongs to the bot (mentions,
moderation and reports use the real bot); clients show the alias with a mask icon and the tooltip
"Posted by BOT as ALIAS". Only bot tokens may set an alias; a user or session token gets 403 alias_bots_only (APIError), also for alias=None and on the gateway chat_send.
from velora_api import Instance, make_alias
bot = Instance("https://chat.ikelene.net", bot_token=os.environ["BOT_TOKEN"])
sent = bot.bot_send("CHANNEL_ID", "Welcome in!", alias={"name": "Narrator", "avatar_url": "https://example.com/n.png"})
print(sent["alias"]) # {"name": "Narrator", "avatar_url": "https://example.com/n.png"}
bot.bot_edit("CHANNEL_ID", sent["id"], alias=make_alias("Storyteller")) # change it
bot.bot_edit("CHANNEL_ID", sent["id"], alias=None) # clear it
name is 1-32 characters with no line breaks and avatar_url is optional (None or "" = no avatar), https:// only, at most 500
characters, no user:pass@, no whitespace; leaving alias out of bot_edit keeps the current one; make_alias checks both before the request (ValueError). alias= is also accepted by
BotProgram.send(...) (through its fields), Instance.send_message, Instance.edit_message and
InstanceGateway.send_message. Messages are plain dicts in this library, so read message["alias"], which is
None or {"name", "avatar_url", "unmarked"}; message["author"] is always the real bot.
Unmarked aliases: when an Ember server's owner turns on Server settings > Danger zone > Advanced > "Allow unmarked aliases"
(advanced.unmarked_aliases, owner only) and the bot has the "Use unmarked aliases" role permission (4096) or Administrator,
its aliases come back with "unmarked": True and, when the name matched exactly one member of that server, "member_id"
(a principal on the client API, a numeric id on the bot API). Clients then show the alias like a normal member message. Nothing
changes in this library: the fields are just more dict keys, and message["author"] is still the real bot.
Complete reference
https://nookara.cc/guides/velora-api
The source catalogue records the live API on 2026-10-19, including staff routes. Not every input has a strict JSON Schema and not every privileged operation is tested live. The server remains authoritative. Internal bridge/payment integration credentials and deployment callbacks are not public client APIs.
Changes in 0.6.1
- Fix
bad_signaturewhen sending signed messages after the move to Nookara:enable_signing()now signs with the issuer the instance announces (/.well-known/bonfirevelora.issuer, falling back tohttps://velora.ikelene.net) instead of the base URL you logged in through.Instance.issuer()returns it, andverify_message()accepts the announced issuer first, then the other known Nookara origins.
Changes in 0.6.0
- Velora is now Nookara: the default base URL is
https://nookara.cc(andwss://nookara.cc/for realtime).velora.ikelene.netkeeps working for now; passbase_url=/url=to keep using it. The import namevelora_apiis unchanged, and a thinnookarapackage re-exports it (import nookara).
Changes in 0.5.10
- DM files no longer go to catbox.moe.
upload_dm_file(path, destination="temp" | "account")uploads to Nookara's temporary storage (POST /chat/media/temp, links athttps://nookara.cc/t/<id>/<name>, the expiry resets on every load) or to your account storage (POST /chat/media/account).upload_destinations()/get_chat_media_destinations()says what's available and how much account space is left.post_chat_media_external()still works and now uploads to temporary storage too.
Changes in 0.5.9
- Realtime: Nookara sends
user_update {user, changed, previous_username?, v}on the user socket whenever someone you can see changes their profile (picture, banner, colour, pronouns, bio, website, country, username). It arrives throughasync for event in socketlike any other frame; patch cached copies byuser["id"]. No API change.
Changes in 0.5.8
- Bot aliases:
alias=onInstance.bot_send,Instance.bot_edit(alias=Noneclears),Instance.send_message,Instance.edit_messageandInstanceGateway.send_message, plusmake_alias(name, avatar_url=None)which checks the limits.bot_editnow takescontentas optional so an alias can be changed alone. Nothing else changed.
Changes in 0.5.7
- Notifications: new generated methods for
/account/notifications(list, unread count, read, unread, delete),/account/notification-settings(GET, PATCH) and the staffPOST /admin/notifications/send, helpersnotifications(),unread_notifications(),mark_notifications_read(),mark_notifications_unread(),delete_notification(),notification_settings(),set_notification_settings(), andRealtimeClient.notification_events()withNOTIFICATION_EVENT_TYPES(notification_new,notifications_updated). - System DMs (
from_systemmessages) are no longer created; those messages are notifications now.
Changes in 0.5.1
- New
get_chat_media_limits(): the real upload limits (server_mbfor files sent in a conversation,saved_mbfor files kept on your Nookara account).get_chat_unfurl()can now returnpaymentcards for Nookara payment links. Nothing else changed.
Changes in 0.5.0
- New
BotClient(bot token) with bot payment helpers:create_payment_request,get_payment_request,find_payment_request,list_payment_requests,cancel_payment_request,wait_for_payment,is_paid, plus thePaymentErrorfamily. The four/bot/payment-requestsroutes are also generated methods (post_bot_payment_requests, ...). - Docs for bot presence and staff test installs (
test: true). No behaviour changes elsewhere.
Changes in 0.4.0
- Scopes are only requested:
login(scopes=...)sends them as an optional request, the newscopesmodule andClient.granted_scopes()/has_scope()/require_scope()report what the person allowed, and calls without a scope raiseScopeError(anAPIErrorsubclass). - Instance scopes (
chat,voice,media,sign) from the ticket:Instance.scopes,has_scope(),sign="auto", earlyScopeErrorfor uploads. - New:
Bonfire(your saved servers),BotProgram(a bot on every instance it is installed on),verify_message, invites, join requests, media import, channel and role helpers, game/bridge/admin helpers, instance info and build, Nookara helpers for profiles, keys, JWKS, scopes, import jobs, default server and push preferences. - 458 Nookara and 169 instance operations as named methods.
- Legacy
Client.servers()/server()/messages()/send_message()warn that Nookara-hosted chat is read-only.
Encrypted direct messages
DMs are end-to-end encrypted, so Client.send_dm no longer sends plain text. A client makes its own keys and asks the person to approve it in Ember:
from velora_api import EncryptedDMs
dms = EncryptedDMs(client, key_file="nookara-dms.json") # pip install "nookara[signing]"
if not dms.linked():
code = dms.request_link()
print("In Ember: Settings > Privacy > Approve a device, and enter", code)
dms.wait_for_approval(code)
dms.send("alice", "Hello!")
for m in dms.messages("alice"):
print(m["from"], dms.open(m))
Before linking, the Direct Messages permission only gets locked messages and who/when. key_file holds your private keys: protect it like a password. People can unlink your client any time. Someone aged 13-15 can't give this permission to other people's Apps.
Metadata
Release files for nookara 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| nookara-0.8.0.tar.gz | 123.5 kB | Without provenance | Details for nookara-0.8.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| nookara-0.8.0-py3-none-any.whl | Python 3 | none | any | Details for nookara-0.8.0-py3-none-any.whl |
Total release size: 239.2 kB
Release files / nookara-0.8.0.tar.gz
| Download URL | nookara-0.8.0.tar.gz |
|---|---|
| Size | 123.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
043e8df036a3f77c22270713bf0dd56eb087d93700a6d7362771256441a3104c
|
|
BLAKE2b-256 checksum How to use checksums |
fec821dbb2221fc97741e2a8000d53070a32e994f5f332bf8c51127a1b95f82a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / nookara-0.8.0-py3-none-any.whl
| Download URL | nookara-0.8.0-py3-none-any.whl |
|---|---|
| Size | 115.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a554b0370a33849de331e2249f13d4756fffc81624e74d6c3c28d1ad151126b0
|
|
BLAKE2b-256 checksum How to use checksums |
46feb4d091971a1fd6701ba44c6805c4bdb0f2a458b457760c58b9fb1e930a1f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Security for nookara 0.8.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.