/** * Selfbot token helper — get a user-account token via Discord Remote Auth, * WITHOUT devtools, a password, or a second screen. * * How it works: Discord's "scan QR to log in" is just a link of the form * https://discord.com/ra/. This script asks Discord for that link and * prints it. Open the link on a phone that has the Discord app installed and * approve the "New login" prompt — the token is sent back here and written to * the repo-root .env as DISCORD_SELFBOT_TOKEN. * * bun run token # from bot/ * * ⚠️ The token belongs to whatever account is logged into the Discord app that * APPROVES the request. Switch the app to a BURNER account first — never * approve with your main account (selfbot use can get that account banned). * * ⚠️ The link expires in ~2 minutes. Approve promptly. */ import { readFileSync, writeFileSync, existsSync } from "node:fs"; import { fileURLToPath } from "node:url"; import QRCode from "qrcode"; // Optional native peer dep; only needed for this helper. // @ts-ignore - provided by discord.js-selfbot-v13 (optionalDependency) import { DiscordAuthWebsocket } from "discord.js-selfbot-v13"; const ENV_PATH = fileURLToPath(new URL("../../.env", import.meta.url)); // Where to write the scannable QR image (so it can be sent to the user). const QR_OUT = process.env.QR_OUT || "/tmp/javis_qr.png"; function upsertEnv(key: string, value: string) { let lines: string[] = []; if (existsSync(ENV_PATH)) { lines = readFileSync(ENV_PATH, "utf8").split("\n"); } const idx = lines.findIndex((l) => l.startsWith(`${key}=`)); if (idx >= 0) lines[idx] = `${key}=${value}`; else { if (lines.length && lines[lines.length - 1] !== "") lines.push(""); lines.push(`${key}=${value}`); } writeFileSync(ENV_PATH, lines.join("\n")); } async function main() { const ws: any = new DiscordAuthWebsocket(); ws.on("ready", async () => { // Render the auth URL as a scannable QR PNG. Show it on a SECOND screen // (PC monitor) and scan it with the Discord mobile app's QR scanner. try { await QRCode.toFile(QR_OUT, ws.AuthURL, { width: 512, margin: 2 }); } catch (e) { console.error("QR 생성 실패:", e); } console.log("\n================ DISCORD REMOTE LOGIN ================"); console.log("QR_IMAGE: " + QR_OUT); console.log("AUTH_URL: " + ws.AuthURL); console.log("\nPC/두 번째 화면에 위 QR을 띄우고, 디스코드 모바일 앱의 QR 스캐너로 스캔하세요"); console.log("(버너 계정으로 로그인된 상태!). 승인하면 토큰이 자동 저장됩니다."); console.log("링크는 약 2분 후 만료됩니다."); console.log("=====================================================\n"); }); ws.on("pending", (user: any) => { console.log(`[remote-auth] 스캔됨: ${user?.username ?? "?"} — 폰에서 '승인'을 눌러주세요.`); }); ws.on("finish", (token: string) => { upsertEnv("DISCORD_SELFBOT_TOKEN", token); console.log(`[remote-auth] ✅ 토큰 수신 — ${ENV_PATH} 의 DISCORD_SELFBOT_TOKEN 에 저장했습니다.`); ws.destroy?.(); process.exit(0); }); ws.on("cancel", () => { console.log("[remote-auth] ❌ 사용자가 취소했습니다."); process.exit(1); }); ws.on("closed", () => { console.log("[remote-auth] 연결 종료(만료 또는 닫힘). 다시 실행하세요."); process.exit(1); }); await ws.connect(); } main().catch((e) => { console.error("remote-auth 실패:", e); process.exit(1); });