Public reference for Nighty scripts, native UI scripting, slash commands, Discord Components V2, and CUI.
Quick start · Documentation · Examples · Troubleshooting
Maintained by: rico · 1361736124858630274
Note
Nighty injects its scripting objects at runtime. Do not install or import nighty or discord.py inside a NightyScript.
| Guide | Covers |
|---|---|
| Python 3.14 runtime | Version support, compatibility changes, dependencies, and local checks |
| Native UI scripting | Tabs, containers, cards, groups, controls, tables, callbacks, and responsive layouts |
| Native slash commands | Typed options, groups, autocomplete, themed replies, and sync |
| Discord Components V2 | Native layouts, containers, buttons, sections, and callbacks |
| CUI and custom tabs | Custom HTML elements, callbacks, pagination, and browser reloads |
| Reload-safe scripts | Tasks, listeners, sockets, cleanup, and shared runtime state |
| Troubleshooting | Common load, interaction, CUI, storage, and event-loop failures |
| Example scripts | Complete files ready to inspect and adapt |
Nighty 2.6.2 runs scripts on Python 3.14 inside its own process. Nighty supplies bot, bot_app, nightyScript, Tab, UI, configuration helpers, theme helpers, and Discord objects.
Do not import Nighty or Discord packages:
# Unsupported
import discord
import nighty
from discord import *
from nighty import *Use standard-library imports and Python 3.14-compatible dependencies documented by the script. Keep imports inside the decorated function when Nighty callbacks need them. Nighty may execute a script with separate global and local namespaces. Read Python 3.14 runtime compatibility before adding a package.
@nightyScript(
name="Channel Notes",
author="rico",
description="Save a note for the current channel",
usage="<p>channelnote <text>"
)
def channel_notes():
"""
CHANNEL NOTES
COMMANDS:
<p>channelnote <text> - Save a note for the current channel
"""
@bot.command(
name="channelnote",
usage="<text>",
description="Save a note for the current channel"
)
async def channel_note(ctx, *, text: str):
await ctx.message.delete()
updateConfigData(f"channel_note_{ctx.channel.id}", text.strip())
await ctx.send("Saved the channel note.", silent=True)
channel_notes()Call the decorated function at the end of the file. Nighty will not register the script until that call runs.
- Use
@bot.commandfor prefix commands. - Use
@slashCommandfor native application commands. - Use
TabandUIfor native Nighty settings pages. - Add CUI when a native page needs a custom control or overlay.
- Use Components V2 for structured Discord interaction responses.
Use Nighty's configuration store for booleans, strings, and numbers:
enabled = getConfigData().get("channel_notes_enabled", True)
updateConfigData("channel_notes_enabled", False)Prefix keys with the script name. This prevents collisions with other scripts.
Use a JSON file for lists and nested data:
from pathlib import Path
import json
DATA_DIR = Path(getScriptsPath()) / "json"
DATA_FILE = DATA_DIR / "channel_notes.json"
def load_notes():
DATA_DIR.mkdir(parents=True, exist_ok=True)
try:
with DATA_FILE.open("r", encoding="utf-8") as file:
data = json.load(file)
return data if isinstance(data, dict) else {"notes": {}}
except (FileNotFoundError, json.JSONDecodeError, OSError):
return {"notes": {}}
def save_notes(data):
DATA_DIR.mkdir(parents=True, exist_ok=True)
try:
with DATA_FILE.open("w", encoding="utf-8") as file:
json.dump(data, file, indent=2, ensure_ascii=False)
return True
except OSError as error:
print(f"[Channel Notes] Save error: {error}", type_="ERROR")
return FalseStore tokens and webhook URLs outside source files. Do not commit runtime JSON files.
@bot.command(
name="quote",
aliases=["q"],
usage="<message>",
description="Send a quoted message"
)
async def quote_command(ctx, *, message: str):
await ctx.message.delete()
message = message.strip()
if not message:
await ctx.send("Add a message to quote.", silent=True)
return
await ctx.send(f"> {message}")<p> in usage text represents the prefix set in Nighty. Scripts do not parse the prefix.
Use one command with subcommands when the actions share a purpose:
@bot.command(name="notes", usage="<add|list|clear> [text]")
async def notes_command(ctx, *, args: str = ""):
await ctx.message.delete()
action, _, value = args.strip().partition(" ")
action = action.lower()
if action == "add" and value.strip():
await ctx.send(f"Saved: {value.strip()}", silent=True)
elif action == "list":
await ctx.send("No saved notes.", silent=True)
elif action == "clear":
await ctx.send("Cleared saved notes.", silent=True)
else:
await ctx.send("Use `notes add`, `notes list`, or `notes clear`.", silent=True)@bot.listen("on_message")
async def message_listener(message):
if message.author.id == bot.user.id:
return
if getattr(message.author, "bot", False):
return
if "good morning" in message.content.lower():
await message.channel.send("Good morning.", silent=True)Filter events before network calls or file access. A listener runs for each matching Discord event.
Nighty keeps the Python process alive during script reloads. Scripts with listeners, tasks, or Socket.IO callbacks need cleanup code. Read Reload-safe scripts.
await ctx.send("Saved.", silent=True)
await message.channel.send("New event received.", silent=True)Nighty does not support discord.Embed in standard scripts. Use forwardEmbedMethod:
await forwardEmbedMethod(
channel_id=ctx.channel.id,
title="Build report",
content="**Status:** Passed\n**Checks:** 12",
image="https://example.com/report.png"
)Supported arguments are channel_id, title, content, and image.
Components V2 supports containers, text displays, sections, media, and interactive controls. Use a discord_bot.ui.LayoutView when slashReply() does not provide the layout you need. Read Discord Components V2.
Nighty registers application commands with @slashCommand and sends themed replies with slashReply().
@slashCommand(name="status", description="Show the script status")
async def status(interaction: discord_bot.Interaction):
await slashReply(interaction, "Status: ready")Nighty syncs command changes after the script loads. Read Native slash commands for options, subcommands, autocomplete, and custom Components V2 layouts.
Nighty's native UI uses this hierarchy:
Tab
└── CardContainer
└── Card
├── Group
└── UI element
tab = Tab(name="Task Queue", icon="inbox", gap=4)
container = tab.create_container(type="rows", width="full", height="auto", gap=4)
card = container.create_card(width="full", height="auto", gap=3)
card.create_ui_element(UI.Text, content="Task Queue", size="lg", weight="bold")
task_input = card.create_ui_element(
UI.Input,
label="Task",
placeholder="Enter a task",
full_width=True
)
save_button = card.create_ui_element(UI.Button, label="Save", color="primary")
async def save_task():
value = str(task_input.value or "").strip()
if not value:
tab.toast("Task required", "Enter a task name.", "ERROR")
return
tab.toast("Task saved", value, "SUCCESS")
save_button.onClick = save_task
tab.render()Read Native UI scripting for the full component reference, callbacks, tables, validation, responsive layouts, and debugging.
CUI adds custom HTML elements and callable overlays without changing the native parent creation rules. Use it for controls that Nighty's built-in UI classes do not provide. Read CUI and custom tabs.
Use aiohttp for network requests:
import aiohttp
async def fetch_json(url):
timeout = aiohttp.ClientTimeout(total=15)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.get(url) as response:
response.raise_for_status()
return await response.json()Move blocking libraries to an executor:
async def run_blocking(function, *args):
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, lambda: function(*args))Use asyncio.sleep() inside async functions. time.sleep() blocks Nighty's event loop.
print("[Task Queue] Loaded 12 tasks", type_="INFO")
print("[Task Queue] Save failed", type_="ERROR")
print("[Task Queue] Sync complete", type_="SUCCESS")Include the script name and the failed operation. Keep tokens, cookies, and webhook URLs out of logs.
ui_example.py: native layouts, forms, validation, tables, callbacks, and toastsslash_command_example.py: typed commands, groups, autocomplete, and contextscomponents_v2_example.py: native Components V2 counter with buttonscui_custom_element_example.py: custom CUI element and callbackreload_safe_worker.py: task and listener cleanup across reloads
The example-scripts directory also covers JSON storage, configuration, event listeners, API clients, webhooks, selectors, tables, voice-message downloads, and browser automation.
- Nighty scripts cannot build a complete cross-server user database.
- User and member access depends on Discord cache state and channel access.
- Native slash commands depend on Nighty's application bot connection and Discord registration.
- CUI runs in Nighty's browser page. A browser refresh creates a new JavaScript window.
- Discord rate limits apply to messages, command syncs, and interaction callbacks.
Start with the dedicated troubleshooting guide. It covers script load failures, missing slash commands, expired interactions, raw CUI placeholders, browser refresh problems, JSON write errors, and event-loop failures.
When reporting a bug, include:
- the full Nighty console error;
- the action that caused it;
- whether the script was freshly loaded or reloaded;
- whether Nighty Desktop or the web interface was used;
- the smallest script that still reproduces it.
- cpores for CUI
- nes at Nighty for the Components V2 theme builder and schema
- ThatDudePyro/UI-Scripting-Docs for the Nighty UI scripting reference
Released under the GNU General Public License v3.0.
Back to top