Initial commit

This commit is contained in:
Mikolaj Wojciech Gorski 2025-07-21 06:44:37 +02:00
commit 88018eeebf
8 changed files with 2326 additions and 0 deletions

11
.gitignore vendored Normal file
View file

@ -0,0 +1,11 @@
node_modules/
npm-debug.log*
npm-debug.log*
output/
.env
.env.local
.DS_Store
*.log
docker-data
.tmp
.vscode/settings.json

36
Dockerfile Normal file
View file

@ -0,0 +1,36 @@
FROM node:20-slim
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl ca-certificates sudo \
# ── Discord runtime deps ──────────────────
libatomic1 libnotify4 libnspr4 libnss3 libxss1 libgbm1 \
libgconf-2-4 libxtst6 libgtk-3-0 \
# ── GUI / audio / misc ───────────────────
xvfb pulseaudio openbox \
tigervnc-standalone-server tigervnc-common fonts-liberation \
x11vnc \
&& rm -rf /var/lib/apt/lists/*
# --- DOWNLOAD DISCORD AS ROOT ---
WORKDIR /opt/preinstall
RUN curl -L https://discord.com/api/download/stable?platform=linux -o discord.deb && \
dpkg -i discord.deb || apt-get -f install -y
RUN useradd --create-home --shell /bin/bash bot && \
adduser bot sudo && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
WORKDIR /opt/bot
COPY package*.json ./
RUN npm install --no-audit --no-fund
COPY . .
RUN chmod +x /opt/bot/entry.sh
RUN chown -R bot:bot /opt/bot
USER bot
ENV DISPLAY=:99
RUN mkdir -p /run/dbus
ENTRYPOINT dbus-daemon --system --fork --nofork --address unix:/run/dbus/system_bus_socket & exec /opt/bot/entry.sh "$@"

60
README.md Normal file
View file

@ -0,0 +1,60 @@
# discord-ai
A headless Docker stack that runs the full Discord desktop client (Electron) on
a virtual X11 desktop and provides:
* A live **VNC** stream (`localhost:5901`)
* A **Puppeteer bridge** for visual inspection / screenshots
* A **discord.js-selfbot** back-end for structured access (tokens, DMs, member
lists, etc.)
---
## Quick-start
1. Clone
```bash
git clone https://github.com/<you>/discord-ai.git
cd discord-ai
```
2. Store your **user token** in a shell variable
```bash
export USER_TOKEN="your_discord_user_token"
```
3. Build & run
```bash
docker compose up --build
```
4. Connect
* **VNC viewer** → `localhost:5901` (no password)
* **API / bot.js** available inside the container on its usual port
(default: 3000, mapped to host)
---
## Files
| Path | Purpose |
| --------------- | ------------------------------------------ |
| `Dockerfile` | Base image with all runtime deps |
| `entry.sh` | Boots `Xvfb`, `pulseaudio`, `openbox`, Discord |
| `bot.js` | Puppeteer __and__ discord.js-self glue |
| `package.json` | ES Module entry + minimal deps |
---
## Clean-up
Stop and remove:
```bash
docker compose down
docker image rm discord-ai_discord-ai
```

48
bot.js Normal file
View file

@ -0,0 +1,48 @@
import puppeteer from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";
import { Client as DiscordClient } from "discord.js-selfbot-v13";
import fs from "fs";
puppeteer.use(StealthPlugin());
const DISCORD_TOKEN = process.env.USER_TOKEN;
(async () => {
const browser = await puppeteer.launch({
executablePath: "/usr/bin/discord", // <-- Add this executable path
headless: false,
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--start-maximized",
],
defaultViewport: null,
});
const [page] = await browser.pages();
console.log("Puppeteer has attached to Discord's browser.");
const client = new DiscordClient({ checkUpdate: false });
client.login(DISCORD_TOKEN);
client.once("ready", () => {
console.log(`Selfbot ready as ${client.user.tag} (${client.user.id})`);
});
client.on("messageCreate", (msg) => {
if (msg.author.id === client.user.id) return;
console.log(`[#${msg.channel.name}] ${msg.author.tag}: ${msg.content}`);
takeScreenshot();
});
async function takeScreenshot() {
await page.screenshot({ path: "/tmp/output/last.png" });
console.log("Took a screenshot, saved to /tmp/output/last.png");
}
process.on("SIGTERM", async () => {
await browser.close();
process.exit(0);
});
})();

20
docker-compose.yaml Normal file
View file

@ -0,0 +1,20 @@
version: "3.8"
services:
discord-ai:
build: .
container_name: discord-ai
cap_add:
- SYS_ADMIN
environment:
# supply the user token at runtime:
USER_TOKEN: "${USER_TOKEN}"
volumes:
# live-peek folder so you can grab screenshots outside the container
- ./output:/tmp/output
ports:
- "5901:5901" # VNC
- "3000:3000" # optional HTTP server if you add one
tmpfs:
- /tmp:size=512M
restart: unless-stopped

25
entry.sh Normal file
View file

@ -0,0 +1,25 @@
#!/bin/bash
set -e
rm -f /tmp/.X99-lock
export PULSE_SERVER=unix:/tmp/pulseaudio.socket
Xvfb :99 -screen 0 1920x1080x24 -ac +extension GLX +render -noreset &
pulseaudio --daemonize --exit-idle-time=-1 --disallow-exit &
openbox
# start a simple vnc server that re-uses the same desktop
x11vnc -display :99 -shared -forever -nopw -rfbport 5901 -bg
# Launch discord with the sandbox disabled. This is critical.
discord \
--no-sandbox \
--disable-dev-shm-usage \
--disable-gpu \
--disable-background-timer-throttling \
--disable-renderer-backgrounding \
--disable-features=GpuProcess $@
sleep 10
exec node bot.js "$@"

2117
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

9
package.json Normal file
View file

@ -0,0 +1,9 @@
{
"type": "module",
"dependencies": {
"puppeteer-core": "^21.0.0",
"discord.js-selfbot-v13": "^3.0.0",
"puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2"
}
}