Build Interactive 3D Worlds With GPT-6 & Blender

TL;DR
Your team already lives in Discord. A slash command, a headless OpenCode agent, and a persistent Railway service add up to a bot that answers questions about your repository in the channel everyone already watches. The complete build, start to finish.
The interesting agent work happens in a private tab, and the rest of the team finds out after the PR. That is the exact problem the Slack Code channels post names, and it is not only a Slack problem. The people who should ask questions about your codebase are already sitting in your Discord server, and asking "why does this endpoint timeout" in a shared channel is how a team learns.
This guide builds the honest version of a chat agent: a bot with one /ask slash command that takes a task, clones your repository fresh, runs OpenCode in headless mode against that clean copy, and posts the answer back into the channel. The bot never commits anything - it reads, it answers, and optionally hands back a diff for a human to apply. It lives on Railway as a persistent service, the honest hosting fit: a Discord bot keeps an outbound connection to Discord's gateway open all day, so it needs an always-on process, not a server that wakes up per request.
We run a fleet of this shape for parts of this site - scheduled agents, webhook agents, and chat surfaces reporting back. The mechanics below are the portable core: nine steps, under an hour, every step ending in something you can run.
| Resource | Description |
|---|---|
| OpenCode Docs | Install, provider keys, and the opencode run non-interactive mode |
| OpenCode CLI reference | run, --model, --dir, and environment variables |
| discord.js Docs | Version and Node.js requirement, slash command examples |
| discord.js Guide: Application Setup | Creating the bot application and getting its token |
| discord.js Guide: Adding Your App | Invite link scopes and permissions |
| Discord: Receiving and Responding to Interactions | The 3-second and 15-minute response windows |
| Discord: Message | The 2000-character message content limit |
| Railway Services | Persistent services, Dockerfile builds, ephemeral storage |
| Railway Variables | Secrets and configuration for your service |
| Railway Pricing Plans | Plan caps, per-resource rates, the $5 trial grant |
Head to the Discord Developer Portal, click New Application, name it something your team will recognize, and copy the token from the Bot tab. The discord.js guide's application setup walks the exact clicks. Treat the token like a password: it is the bot's login, and it will live only in Railway Variables, never in your repository.
Next, invite the bot from the OAuth2 page using Discord's URL generator. Select the bot and applications.commands scopes - the adding your app page explains why that pair matters: bot puts the user in your server, applications.commands lets it carry slash commands. Give it the smallest permission set that works: Send Messages, Read Messages/View Channels, and Use Slash Commands is enough for this build. Open the generated link, pick your server, and authorize.
Runnable check: the bot appears in your server's member list.
discord.js is the library; the current release at the time of writing is 14.27.0 and it requires Node.js 24.17.0 or newer (as of September 21, 2026), so anything on the current Node LTS line is fine. Scaffold and install:
mkdir discord-agent && cd discord-agent
npm init -y
npm install discord.js
Now the two files. First the command registration - slash commands are registered against the Discord API, not against your server, via REST and Routes:
// register-commands.js
import { REST, Routes } from 'discord.js';
const commands = [{
name: 'ask',
description: 'Ask the coding agent to investigate the repository',
options: [{
name: 'task',
type: 3,
description: 'What should the agent do?',
required: true,
}],
}];
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
await rest.put(Routes.applicationCommands(process.env.DISCORD_APP_ID), { body: commands });
console.log('Registered /ask');
Run it once with your application ID (the snowflake from the Portal's General Information tab) and bot token:
DISCORD_APP_ID=123456789012345678 DISCORD_TOKEN=your-token node register-commands.js
Runnable check: type / in any channel and /ask appears in the picker.
The receiving and responding docs pin the two numbers that shape every chat-agent design: you must send an initial response within 3 seconds of receiving the interaction, and the interaction token you respond with stays valid for 15 minutes (as of September 21, 2026). An agent run takes longer than 3 seconds, so you defer immediately - the user sees Discord's "thinking" state - and edit that message when the run finishes:
// agent.js
import { Client, Events, GatewayIntentBits, MessageFlags } from 'discord.js';
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand() || interaction.commandName !== 'ask') return;
if (!ALLOWED_CHANNEL_IDS.length || !ALLOWED_CHANNEL_IDS.includes(interaction.channelId)) {
return interaction.reply({
content: 'Ask me in an allowlisted channel.',
flags: MessageFlags.Ephemeral,
});
}
await interaction.deferReply();
const task = interaction.options.getString('task');
const answer = await runAgent(REPO, task);
await interaction.editReply(truncate(answer));
});
await client.login(process.env.DISCORD_TOKEN);
The Guilds intent is all a slash-command bot needs - no privileged message-content intent, no reading chat history. deferReply() sends callback type 5 (DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE), the "acknowledge now, edit later" shape those timing rules demand.
Runnable check: run DISCORD_TOKEN=... node agent.js locally and the bot shows online in your server.
This is the same runner pattern as our cron automation guide with the interactive front door swapped in: one fresh clone, one opencode run, a soft gate, no commits:
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
const exec = promisify(execFile);
export async function runAgent(repo, task) {
const dir = await mkdtemp(path.join(tmpdir(), 'agent-'));
try {
await exec('git', ['clone', '--depth=1', `https://github.com/${repo}.git`, dir],
{ timeout: 120_000 });
const { stdout } = await exec('opencode', [
'run',
'--dir', dir,
'--model', AGENT_MODEL,
task,
], { timeout: 600_000, maxBuffer: 16 * 1024 * 1024 });
return stripAnsi(stdout).trim();
} finally {
await rm(dir, { recursive: true, force: true });
}
}
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
What each line is doing, and why it is the safe version:
mkdtemp plus a finally cleanup means no state accumulates between questions - the same guardrail the scheduled version relies on.--depth=1. A shallow clone keeps even large repos fast to fetch.opencode run --dir. The CLI reference documents run as non-interactive mode and --model as provider/model form (opencode/deepseek-v4-flash, the budget model our DeepSeek V4 Flash guide benchmarks, is the sensible AGENT_MODEL default).Runnable check (local): DISCORD_TOKEN=... node agent.js, then /ask task: "Summarize the purpose of this codebase in 4 bullets" against a small public repo. You should see the agent's answer appear in the channel a minute or two later.
From the archive
Sep 20, 2026 • 8 min read
Sep 19, 2026 • 8 min read
Sep 16, 2026 • 11 min read
Sep 16, 2026 • 6 min read
Two realities of a chat surface: an agent is expensive, and anyone with access can invoke it. Both get cheap answers.
A queue. Two people slamming /ask at once means two concurrent agent runs on your smallest instance. Keep a simple in-process promise queue so exactly one runAgent executes at a time; the interaction replies "Queued at position N" for anything behind.
A channel allowlist. ALLOWED_CHANNEL_IDS (comma-separated environment variable) restricts which channels the bot answers in; anything else gets an ephemeral refusal. The agent reads your codebase, so the surface that can reach it should be smaller than the whole server. This is also the deck-privilege rule from the fleet economics post: worker agents get cheap models, judges get strong ones, and a chat bot that answers questions is a worker.
Dockerfile in the repo root - install git for the clone step, then OpenCode via npm because that is the documented Node install path and it lands on PATH automatically:
FROM node:24-slim
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN npm install -g opencode-ai
WORKDIR /app
COPY package.json ./
RUN npm install --omit=dev
COPY agent.js .
CMD ["node", "agent.js"]
Push the repo to GitHub, then create a new project on Railway and Connect Repo. Railway detects the Dockerfile and builds from it automatically, per the services reference - every push to the linked branch rebuilds and redeploys.
Under the service's Variables tab (variables reference), set:
| Variable | Value |
|---|---|
DISCORD_TOKEN | the bot token from Step 1 |
REPO | owner/name the bot should clone |
AGENT_MODEL | opencode/deepseek-v4-flash or your provider/model |
ALLOWED_CHANNEL_IDS | comma-separated channel snowflakes |
| provider key | OpenCode loads provider API keys from the environment, so add whatever your model provider expects |
One thing you do not need: a public domain. Your bot dials out to Discord's gateway and keeps the connection open, so Railway's public networking is irrelevant here - you just need the process to stay alive, which is what a persistent service is for.
Cost context, pinned to the pricing plans reference (as of September 21, 2026): a new account gets a $5 trial grant, the Free plan caps a service at 0.5 GB RAM and 1 vCPU which is tight for an agent container, and the $5/month Hobby plan includes $5 of usage with resources billed at $10 per GB of RAM and $20 per vCPU per month, plus $0.05 per GB of egress. A small always-on bot lands within a few dollars of usage a month, and the clone directories live in ephemeral storage - 1 GB on Free, 100 GB on paid - which is fine because every run cleans up after itself.
Runnable check: deploy, open the service logs, watch the Discord-ready line - the bot shows online in the server member list, and /ask works from the deployment.
A bot in a channel is a public input surface, so the safety comes from the pipeline, not from trusting the model:
A bot in any allowlisted channel can ask questions about the repository, backed by a real agent on a real clone, reachable from phone, web, or desktop with no setup beyond a slash command. Architecture, endpoints, tests, and error paths all work; anything that would require writing code comes back as analysis, which is exactly what a team chat should do.
Three extensions that stay in the same shape, each an hour or less:
git diff after the agent finishes and upload it as a message attachment (within Discord's message limits; attachments carry their own size cap). A human reviews and applies. The bot still never commits.exec call; changing AGENT_MODEL in a Variable re-platforms the whole bot without a code change. The harness post is the general argument for why this seam matters.You can, but do not start there. Read-only is the correct default for a shared surface: the bot's worst output is a wrong answer, not a bad push. When a team eventually needs commits, the human-in-the-loop pattern from our issue-to-PR guide is the safer shape.
Neither. The bot keeps an outbound connection to Discord's gateway, and the heavy lifting happens at your model provider's API. Railway just keeps a small Node container alive - a GPU would be wasted money here.
Model tokens plus hosting. Hosting on Railway: a new account's $5 trial credit covers the build, then the $5/month Hobby plan includes $5 of usage with resources at $10 per GB of RAM and $20 per vCPU per month. Model cost with a budget model like DeepSeek V4 Flash is cents per question, per the scheduled agent cost math. The unbounded risk is loops, which is what the timeout exists for.
Two Discord rules make that awkward: you must acknowledge an interaction within 3 seconds or the token is invalidated, and interaction tokens expire after 15 minutes (receiving and responding docs, as of September 21, 2026). Deferring immediately and keeping agent runs under 10 minutes fits both windows with room to spare. Answer messages also get truncated to Discord's 2000-character content limit.
Vendor chat surfaces like Slack Code bundle their own agents and permissions into a hosted product. This build is yours: any harness, any model provider, your allowlist, your repo, one Dockerfile. If your team lives in Discord, a self-hosted bot is the way to get the same idea there - and it stays standing if vendor access changes.
| Source | URL |
|---|---|
| OpenCode Docs | https://opencode.ai/docs/ |
| OpenCode CLI reference | https://opencode.ai/docs/cli/ |
| discord.js Docs | https://discord.js.org/docs |
| discord.js Guide: Application Setup | https://discordjs.guide/legacy/preparations/app-setup |
| discord.js Guide: Adding Your App | https://discordjs.guide/legacy/preparations/adding-your-app |
| Discord: Receiving and Responding to Interactions | https://discord.com/developers/docs/interactions/receiving-and-responding |
| Discord: Message resource | https://docs.discord.com/developers/resources/message |
| Railway Services reference | https://docs.railway.com/reference/services |
| Railway Variables | https://docs.railway.com/develop/variables |
| Railway Pricing Plans | https://docs.railway.com/reference/pricing/plans |
| Railway Cron Jobs | https://docs.railway.com/reference/cron-jobs |
Some links to tools above are referral links - see our affiliate disclosure.
Last updated: September 21, 2026
Read next
An agent CLI plus a cron schedule turns recurring dev chores into background work: dependency bumps, doc freshness checks, morning briefs. The pattern, the guardrails, and where to run it - your own hardware or a cloud host.
11 min readThe most common trigger for an AI coding agent is not a clock, it is an event. A GitHub webhook, a Railway service, and OpenCode headless add up to a repo where a labeled issue gets a real pull request without anyone at the keyboard. The full build, start to finish.
10 min readSlack Code puts coding agents in dedicated Slack channels with diffs, live HTML previews, and an audit log. Here is when that beats a local Claude Code session, Cursor, or OpenCode - and when to skip it.
7 min readTechnical content at the intersection of AI and development. Building with AI agents, Claude Code, and modern dev tools - then showing you exactly how it works.
OpenAI's open-source terminal coding agent built in Rust. Runs locally, reads your repo, edits files, and executes comma...
View ToolAnthropic's agentic coding CLI. Runs in your terminal, edits files autonomously, spawns sub-agents, and maintains memory...
View ToolAI coding platform built for large, complex codebases. Context Engine indexes 500K+ files across repos with 100ms retrie...
View ToolOpen-source autonomous coding agent inside VS Code. Creates files, runs commands, and can use a browser for UI testing a...
View ToolDescribe your company and agent teams handle operations.
View AppScore every coding agent on your own tasks. Catch regressions in CI.
View AppSpec out AI agents, run them overnight, wake up to a verified GitHub repo.
View AppWhat MCP servers are, how they work, and how to build your own in 5 minutes.
AI AgentsInstall Ollama and LM Studio, pull your first model, and run AI locally for coding, chat, and automation - with zero cloud dependency.
Getting StartedStep-by-step guide to building an MCP server in TypeScript - from project setup to tool definitions, resource handling, testing, and deployment.
AI Agents
In this episode, we explore the newly released GPT-5 Codex by OpenAI, a specialized version of GPT-5 designed for agentic coding tasks. Codex offers advanced features, including enhanced code...

Try Zoer today: https://zoer.ai/ the all-in-one full-stack tool that combines Lovable, Supabase, and Netlify in one. In this video, discover Zoer, a cutting-edge platform that enables you...

Check out Trae here! https://tinyurl.com/2f8rw4vm In this video, we dive into @Trae_ai a newly launched AI IDE packed with innovative features. I provide a comprehensive demonstration...

An agent CLI plus a cron schedule turns recurring dev chores into background work: dependency bumps, doc freshness check...

The most common trigger for an AI coding agent is not a clock, it is an event. A GitHub webhook, a Railway service, and...

Slack Code puts coding agents in dedicated Slack channels with diffs, live HTML previews, and an audit log. Here is when...

A long-running coding agent is only useful if the environment around it can queue tasks, capture logs, checkpoint state,...

Five managed-agent providers, five pricing models, zero unified cost attribution. If you're running agents overnight, yo...

DeepSeek shipped the official V4 Flash release on July 31, 2026. The re-post-trained 0731 build beats V4-Pro-Preview on...

New tutorials, open-source projects, and deep dives on coding agents - delivered weekly.