diff --git a/bot/package.json b/bot/package.json index 11a00e0..7ccf167 100644 --- a/bot/package.json +++ b/bot/package.json @@ -7,6 +7,7 @@ "scripts": { "start": "bun run src/index.ts", "register": "bun run src/register-commands.ts", + "token": "bun run src/get-token.ts", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/bot/src/get-token.ts b/bot/src/get-token.ts new file mode 100644 index 0000000..28b2431 --- /dev/null +++ b/bot/src/get-token.ts @@ -0,0 +1,79 @@ +/** + * 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"; +// 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)); + +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", () => { + console.log("\n================ DISCORD REMOTE LOGIN ================"); + console.log("아래 링크를 디스코드 앱이 깔린 폰에서 여세요 (버너 계정으로 로그인된 상태!):"); + console.log("\nAUTH_URL: " + ws.AuthURL + "\n"); + 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); +});