Skip to content

Repository files navigation

Discript

Discript is a scripting language and CLI for safe, programmatic Discord automation. It is designed for developers, AI agents, and coding harnesses that need repeatable commands, composable scripts, structured results, and explicit control over side effects.

At its core, Discript parses and runs .ds programs or commands from files, stdin, or inline CLI input. The same engine can connect directly to Discord for one-shot work, reuse a long-lived daemon connection over a local socket, or expose and consume the engine through MCP stdio and HTTP/HTTPS transports.

Requirements

  • Node.js 26 or newer
  • A Discord bot token in DISCORD_TOKEN
  • The bot must be able to view the target guilds and channels

Setup

npm install
node -e "require('node:fs').copyFileSync('.env.example', '.env')"
# edit .env with a private Discord bot token

For local installation before the first npm release, use the Git SSH URL or build a tarball:

npm install git+ssh://git@github.com/eliware/discript.git
npm pack
npm install ./eliware-discript-*.tgz

The package is public and publishes to npm from v* Git tags through the standard Eliware release workflow. The npm package name is @eliware/discript because the unscoped discript name is unavailable for publishing.

Documentation

The full documentation map is in docs/README.md, including getting started, the language, CLI and Discord references, agent workflows, developer guides, operations, and contribution guidance.

List guilds with a direct CLI command:

npm start -- guilds list --json

Read-only commands and supported one-shot mutations can use REST without opening a Gateway session:

npm start -- --rest guilds list --json
npm start -- --rest channels list --guild <guild-id> --json
npm start -- --rest messages send --channel <channel-id> --content "hello"
npm start -- --rest channels delete --channel <channel-id> --yes

For repeated Gateway-backed commands or scripts, start the shared local broker once:

npm start -- daemon start
npm start -- --broker guilds list --json
npm start -- --broker script.ds
npm start -- daemon stop

Discover supported direct commands or generate shell completion scripts:

npm start -- commands list --json
npm start -- completion bash > discript-completion.bash
npm start -- completion zsh
npm start -- completion fish

Inspect the connected bot identity:

npm start -- bot get --json

Common singular and abbreviated resource names are accepted, such as msg send, chan list, and guild list. Unknown commands include nearby command suggestions; JSON errors expose them under details.suggestions. Machine-readable errors include error, code, and exitCode. Discord API failures use the stable DISCORD_API_ERROR code with exit status 5; sanitized request metadata may appear under details.

List channels in a guild:

npm start -- channels list --guild <guild-id> --json

Look up a guild or channel:

npm start -- guilds get --guild <guild-id> --json
npm start -- channels get --channel <channel-id> --json

Discover members and roles:

npm start -- members list --guild <guild-id> --json
npm start -- roles list --guild <guild-id> --json

Read guild invites, emojis, and stickers:

npm start -- invites list --guild <guild-id> --json
npm start -- emojis list --guild <guild-id> --json
npm start -- stickers list --guild <guild-id> --json

Emoji and sticker lifecycle operations require --yes and support --dry-run previews:

npm start -- emojis create --guild <guild-id> --name wave --file ./wave.png --yes --json
npm start -- emojis delete --guild <guild-id> --emoji <emoji-id> --yes --json
npm start -- stickers create --guild <guild-id> --name wave --file ./wave.png --tags wave --yes --json
npm start -- stickers delete --guild <guild-id> --sticker <sticker-id> --yes --json

Channel webhooks support guarded listing, creation, and deletion:

npm start -- webhooks list --channel <channel-id> --json
npm start -- webhooks create --channel <channel-id> --name automation --yes --json
npm start -- webhooks delete --channel <channel-id> --webhook <webhook-id> --yes --json

Channel permission overwrites can be inspected or changed with comma-separated Discord permission names:

npm start -- permissions list --channel <channel-id> --json
npm start -- permissions set --channel <channel-id> --target <role-or-user-id> --allow ViewChannel,SendMessages --deny ManageMessages --yes --json
npm start -- permissions delete --channel <channel-id> --target <role-or-user-id> --yes --json

Member voice status and guarded controls are available through voice-users:

npm start -- voice-users status --guild <guild-id> --user <user-id> --json
npm start -- voice-users mute --guild <guild-id> --user <user-id> --yes --json
npm start -- voice-users unmute --guild <guild-id> --user <user-id> --yes --json
npm start -- voice-users deafen --guild <guild-id> --user <user-id> --yes --json
npm start -- voice-users undeafen --guild <guild-id> --user <user-id> --yes --json
npm start -- voice-users move --guild <guild-id> --user <user-id> --channel <voice-channel-id> --yes --json
npm start -- voice-users disconnect --guild <guild-id> --user <user-id> --yes --json

Invite creation and deletion are guarded mutations; use --yes (or -y) and preview with --dry-run:

npm start -- invites create --guild <guild-id> --channel <channel-id> --duration 3600 --yes --json
npm start -- invites delete --guild <guild-id> --invite <invite-code> --yes --json

Scheduled events support discovery and guarded lifecycle operations:

npm start -- events list --guild <guild-id> --json
npm start -- events create --guild <guild-id> --name "Town hall" --start "2030-01-01T00:00:00Z" --yes --json
npm start -- events update --guild <guild-id> --event <event-id> --name "Updated" --yes --json
npm start -- events delete --guild <guild-id> --event <event-id> --yes --json

Voice connections support guarded join/leave and status inspection:

npm start -- voice status --guild <guild-id> --json
npm start -- voice join --channel <voice-channel-id> --yes --json
npm start -- voice leave --guild <guild-id> --yes --json

Scripts can declare event handlers. The incoming Discord payload is available as event inside the handler:

on("messageCreate") {
  print(event.content)
}

When a script registers one or more handlers, it remains running until a termination signal is received; --timeout <milliseconds> can bound that lifetime for automation.

Scripts can read and modify the environment of the running Node process. Use property access or the explicit methods:

guildId = env.TEST_GUILD
tokenHint = env.get("DISCRIPT_MODE")
env.set("DISCRIPT_LAST_RUN", "agent")
env.clear("DISCRIPT_TEMP")

Environment values are strings; missing variables return null. Avoid printing secrets such as DISCORD_TOKEN.

Use bounded for-in loops to process result collections:

members = discord.guilds.get("<guild-id>").members.list()
for (member in members) {
  print(member.username)
}

Scripts can schedule asynchronous work and compose concurrent operations:

every(60000) { print("heartbeat") }
after(1000) { print("started") }
values = parallel(sleep(10), sleep(10))

Arrow callbacks can transform and select collections:

names = map(members, member => member.username)
active = filter(members, member => member.roles != null)

Reusable script functions use fn and return:

fn announce(name) {
  return "Hello " + name
}
print(announce("Discord"))

Reusable source files can be loaded into the current script scope:

import "./shared.discript"
print(announce("Discord"))

Threads support inspection and guarded lifecycle operations:

npm start -- threads list --channel <channel-id> --json
npm start -- threads create --channel <channel-id> --name "topic" --yes --json
npm start -- threads archive --channel <channel-id> --thread <thread-id> --yes --json

Role changes require explicit approval:

npm start -- roles add --guild <guild-id> --user <user-id> --role <role-id> --yes --json
npm start -- roles remove --guild <guild-id> --user <user-id> --role <role-id> --yes --json
npm start -- roles create --guild <guild-id> --name "Helper" --yes --json
npm start -- roles update --guild <guild-id> --role <role-id> --name "Helper" --yes --json
npm start -- roles delete --guild <guild-id> --role <role-id> --yes --json

Moderation operations also require explicit approval:

Moderation also rejects bot, owner, self, and targets at or above the bot’s role hierarchy.

npm start -- moderation timeout --guild <guild-id> --user <user-id> --duration 3600000 --reason "reason" --yes --json
npm start -- moderation kick --guild <guild-id> --user <user-id> --reason "reason" --yes --json
npm start -- moderation ban --guild <guild-id> --user <user-id> --reason "reason" --yes --json

Preview or send a message. Writes require explicit approval:

npm start -- messages send --channel <channel-id> --content "Hello" --dry-run --json
npm start -- messages send --channel <channel-id> --content "Hello" --yes --json

Create a test channel with the configured test guild:

npm start -- channels create --guild "$TEST_GUILD" --name discript-test --dry-run --json
npm start -- channels create --guild "$TEST_GUILD" --name discript-test --yes --json

Direct dry-runs validate command-specific required fields and return a structured preview without connecting to Discord:

npm start -- channels create --guild <guild-id> --name preview --dry-run --json

Add --validate to connect, resolve the target, and check permissions while still preventing the mutation:

npm start -- channels create --guild <guild-id> --name preview --dry-run --validate --json

Messages can be read or changed with explicit approval for writes:

npm start -- messages get --channel <channel-id> --message <message-id> --json
npm start -- messages edit --channel <channel-id> --message <message-id> --content "Updated" --yes --json
npm start -- messages delete --channel <channel-id> --message <message-id> --yes --json
npm start -- messages react --channel <channel-id> --message <message-id> --emoji "👍" --yes --json
npm start -- messages pin --channel <channel-id> --message <message-id> --yes --json
npm start -- messages unpin --channel <channel-id> --message <message-id> --yes --json
npm start -- messages bulk-delete --channel <channel-id> --messages <id1,id2> --yes --json

Evaluate source inline:

npm start -- --eval 'guilds = discord.guilds.list(); guilds' --json

Finite commands can be bounded for automation:

npm start -- --timeout 15000 --eval 'discord.guilds.list()' --json

Read source from standard input:

printf '%s\n' 'discord.guilds.list()' | npm start -- --json

For agent pipelines and long-running scripts, use JSONL output:

npm start -- --eval 'print({phase: "start"}); discord.guilds.list()' --output jsonl

Run a script file:

npm start -- examples/list-guilds.ds --json

More agent-oriented templates are included in examples/: safe-channel-workflow.ds demonstrates preview/force/try/exit handling, and event-monitor.ds demonstrates persistent event and timer handlers.

The language supports literals, variables, property access, function calls, sequential statements, functions, conditionals, loops, imports, environment access, async helpers, timers, event handlers, comments, dry-run/approval options, and script-level exit statuses. The CLI and language runtime use the same Discord capability layer. See docs/language/reference.md for the implemented contract.

Development

npm test
npm run lint

The tests are deterministic where possible and do not require a Discord connection. Live smoke commands require a configured DISCORD_TOKEN.

Security

Never commit .env, bot tokens, credential-bearing URLs, or raw Discord payloads containing sensitive data. Mutating operations will require explicit safeguards as the capability surface expands.

Specifications

Product and behavior requirements are maintained in SPEC.md and specs/.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages