diff --git a/.gitignore b/.gitignore index 94c7a2a..f40a76b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ __pycache__/ .venv/ *.egg-info/ poc/frames/ +.env +node_modules/ diff --git a/PLAN.md b/PLAN.md index 7efc5cf..e3522cd 100644 --- a/PLAN.md +++ b/PLAN.md @@ -23,12 +23,32 @@ ### 1단계 · 눈: 화면공유 스트림 수신 ← 지금 - [x] 호스트 도구 확인 (Node 22/ffmpeg/libsodium 가능) - [x] 수신 방법 조사 확정: 셀프봇 프로토콜 레벨 비디오 RTP 수신 -- [ ] **[블로킹] 버리는 디스코드 유저 계정 + 토큰 확보, ToS/밴 위험 수용 확인** +- [x] **버리는 디스코드 유저 계정 + 토큰 확보, ToS/밴 위험 수용 확인** (`.env`, burner tkrmagid_bot) +- [x] **[관문 1 통과] DAVE/MLS(E2EE) 게이트웨이 4017 검증** — 아래 참조 - [ ] 셀프봇으로 음성채널 join → 상대 Go Live 스트림 watch/구독 (비디오 SSRC 획득) -- [ ] 들어오는 비디오 RTP 수신 → 복호화 → depayload(VP8/H264) +- [ ] 들어오는 비디오 RTP 수신 → **DAVE 복호화(daveSession.decrypt)** → depayload(VP8/H264) - [ ] ffmpeg로 디코드 → 프레임(JPEG/PNG) 추출 PoC (진짜 공유화면 수신 검증) - [ ] 프레임 변화 감지(동일 화면 반복 전송 방지) + 소스 인터페이스 +#### 관문 1 (arbiter 지정 fail-fast) — 통과 확정 2026-08-09 +DAVE(MLS) E2EE가 전면 강제라 "토큰만 꽂으면 됨"이 깨졌다는 게 핵심 리스크였음. +`dave/gate.mjs` 로 실측 검증 → **통과**: +- 셀프봇 유저토큰으로 메인 GW IDENTIFY → 음성채널 join → 보이스 GW v8 IDENTIFY(`max_dave_protocol_version=1`). +- 결과: **close 4017 없음**. `SESSION_DESCRIPTION`에서 `dave_protocol_version=1` 협상됨(= 해당 채널 E2EE 활성). +- `@snazzah/davey`(Rust NAPI, DAVE 구현)로 MLS 멤버십 핸드셰이크 완주: + op25 external_sender 수신 → op26 key_package 송신 → op27 proposals 처리(commit+welcome 생성) + → op28 commit_welcome 송신 → op29 announce_commit → `processCommit` → **MLS session ready=true**. +- 5초간 서버 disconnect 없이 멤버십 유지, voicePrivacyCode 산출됨(실제 E2EE 그룹 참여 증명). +- 결론: 셀프봇이 DAVE E2EE 보이스 그룹에 **정식 멤버로 합류 가능**. 옵션 A(직접 수신) 실현 가능 확정. +- 바이너리 프레이밍 확정: 수신 `[u16 seq][u8 op][payload]`; op29/30 payload는 `[u16 transition_id][blob]`; + op27 payload는 `[u8 op_type][proposals]`; 송신 op26/28은 `[u8 op][blob(+welcome)]`. + processProposals에는 채널 내 인식 유저ID(op11 clients_connect)를 넘겨야 함(안 넘기면 UnexpectedUser). + +#### 다음 (관문 통과 후 A 강행) +- 상대의 Go Live 스트림 구독: op **video** 스트림 SSRC 확보(스트림 시청 시그널링) — Go Live 중인 소스 필요. +- UDP로 들어오는 SRTP 비디오 패킷 수신 → RTP 헤더 파싱 → `daveSession.decrypt(userId, VIDEO, packet)` E2EE 복호화 → + 전송암호(aead_aes256_gcm_rtpsize) 복호 → VP8/H264 depayload → ffmpeg 디코드 → 프레임. + ### 3단계 · 두뇌 (하이브리드 비전) - [ ] 로컬 VLM(예: moondream2 / Qwen2-VL-2B) 프레임 1차 이해 + 변화 감지 - [ ] Claude OAuth(Haiku)로 어려운 프레임 에스컬레이션 diff --git a/dave/gate.mjs b/dave/gate.mjs new file mode 100644 index 0000000..dc99bf1 --- /dev/null +++ b/dave/gate.mjs @@ -0,0 +1,321 @@ +// Fail-fast gate (arbiter checkpoint #1): +// Can a selfbot pass the Discord voice DAVE/MLS handshake (no close 4017), +// and does the voice gateway negotiate DAVE + push the external-sender (op25)? +// +// This intentionally stops at "gate evidence": we log the voice IDENTIFY result, +// whether close code 4017 occurs, the negotiated dave_protocol_version, and any +// DAVE opcodes the server pushes. It joins a voice channel muted+deaf and leaves +// immediately after collecting evidence. No media is transmitted. +// +// Usage: DAVE_VER=1 node gate.mjs (declare DAVE support) +// DAVE_VER=0 node gate.mjs (declare DAVE UNsupported -> expect rejection) + +import WebSocket from 'ws'; +import dgram from 'node:dgram'; +import fs from 'node:fs'; +import * as davey from '@snazzah/davey'; + +// --- config --- +const env = Object.fromEntries( + fs.readFileSync(new URL('../.env', import.meta.url), 'utf8') + .split('\n').filter(l => l && !l.startsWith('#') && l.includes('=')) + .map(l => { const i = l.indexOf('='); return [l.slice(0, i).trim(), l.slice(i + 1).trim()]; }) +); +const TOKEN = env.DISCORD_SELFBOT_TOKEN; +const GUILD_ID = process.env.GUILD_ID || '1352269198297923648'; +const CHANNEL_ID = process.env.CHANNEL_ID || '1352269198914621465'; // "일반" +const DAVE_VER = process.env.DAVE_VER != null ? Number(process.env.DAVE_VER) : davey.DAVE_PROTOCOL_VERSION; +const HARD_TIMEOUT_MS = Number(process.env.TIMEOUT_MS || 30000); + +if (!TOKEN) { console.error('no token'); process.exit(2); } + +const t0 = Date.now(); +const log = (...a) => console.log(`[+${String(Date.now() - t0).padStart(6)}ms]`, ...a); +console.log(`davey VERSION=${davey.VERSION} DAVE_PROTOCOL_VERSION=${davey.DAVE_PROTOCOL_VERSION}`); +log(`gate start: declaring max_dave_protocol_version=${DAVE_VER}, channel=${CHANNEL_ID}`); + +const evidence = { + daveVerDeclared: DAVE_VER, + mainReady: false, + voiceServerReceived: false, + voiceIdentifySent: false, + voiceReady: false, + voiceReadyModes: null, + negotiatedDaveVersion: null, + sawExternalSender: false, + sawDaveOpcodes: [], + mlsSessionReady: false, + voiceCloseCode: null, + voiceCloseReason: null, + verdict: 'INCOMPLETE', + notes: [], +}; + +let mainWs, voiceWs, udp; +let daveSession = null; +let voiceState = { session_id: null, token: null, endpoint: null, ssrc: null, ip: null, port: null, mode: null }; +let finished = false; + +function finish(verdict, extra) { + if (finished) return; + finished = true; + evidence.verdict = verdict; + if (extra) evidence.notes.push(extra); + // leave the voice channel politely + try { mainWs?.send(JSON.stringify({ op: 4, d: { guild_id: GUILD_ID, channel_id: null, self_mute: true, self_deaf: true } })); } catch {} + setTimeout(() => { + try { voiceWs?.close(); } catch {} + try { udp?.close(); } catch {} + try { mainWs?.close(); } catch {} + console.log('\n===== GATE EVIDENCE ====='); + console.log(JSON.stringify(evidence, null, 2)); + process.exit(0); + }, 400); +} +setTimeout(() => finish(evidence.verdict === 'INCOMPLETE' ? 'TIMEOUT' : evidence.verdict, `hard timeout ${HARD_TIMEOUT_MS}ms`), HARD_TIMEOUT_MS); + +// ---------- MAIN GATEWAY ---------- +mainWs = new WebSocket('wss://gateway.discord.gg/?v=10&encoding=json'); +let mainHb; +mainWs.on('open', () => log('main gw: open')); +mainWs.on('message', (raw) => { + const p = JSON.parse(raw.toString()); + if (p.op === 10) { + const iv = p.d.heartbeat_interval; + mainHb = setInterval(() => { try { mainWs.send(JSON.stringify({ op: 1, d: null })); } catch {} }, iv); + mainWs.send(JSON.stringify({ op: 2, d: { + token: TOKEN, + capabilities: 16381, + properties: { os: 'Linux', browser: 'Chrome', device: '', system_locale: 'en-US', browser_user_agent: 'Mozilla/5.0', browser_version: '124.0', os_version: '', release_channel: 'stable', client_build_number: 300000 }, + compress: false, + presence: { status: 'invisible', since: 0, activities: [], afk: false }, + }})); + log('main gw: sent IDENTIFY'); + } else if (p.op === 0) { + if (p.t === 'READY') { + evidence.mainReady = true; + log(`main gw: READY as ${p.d.user?.username} (${p.d.user?.id})`); + // join voice channel muted+deaf + mainWs.send(JSON.stringify({ op: 4, d: { guild_id: GUILD_ID, channel_id: CHANNEL_ID, self_mute: true, self_deaf: true } })); + log('main gw: sent Voice State Update (join)'); + } else if (p.t === 'VOICE_STATE_UPDATE' && p.d.user_id) { + if (p.d.session_id) { voiceState.session_id = p.d.session_id; log('VOICE_STATE_UPDATE session_id acquired'); maybeConnectVoice(); } + } else if (p.t === 'VOICE_SERVER_UPDATE') { + voiceState.token = p.d.token; voiceState.endpoint = p.d.endpoint; + evidence.voiceServerReceived = true; + log(`VOICE_SERVER_UPDATE endpoint=${p.d.endpoint}`); + maybeConnectVoice(); + } + } +}); +mainWs.on('close', (c, r) => { log(`main gw: close ${c} ${r}`); clearInterval(mainHb); }); +mainWs.on('error', (e) => log('main gw error', e.message)); + +// ---------- VOICE GATEWAY ---------- +function maybeConnectVoice() { + if (voiceWs || !voiceState.session_id || !voiceState.token || !voiceState.endpoint) return; + const url = `wss://${voiceState.endpoint}/?v=8`; + log(`voice gw: connecting ${url}`); + voiceWs = new WebSocket(url); + let voiceHb, lastSeq = null; + const knownUsers = new Set(['1513862586112671786']); + voiceWs.on('open', () => { + voiceWs.send(JSON.stringify({ op: 0, d: { + server_id: GUILD_ID, + user_id: '1513862586112671786', + session_id: voiceState.session_id, + token: voiceState.token, + max_dave_protocol_version: DAVE_VER, + }})); + evidence.voiceIdentifySent = true; + log(`voice gw: sent IDENTIFY (max_dave_protocol_version=${DAVE_VER})`); + }); + voiceWs.on('message', (raw, isBinary) => { + if (isBinary) { + const buf = Buffer.isBuffer(raw) ? raw : Buffer.from(raw); + // received binary DAVE frame: uint16 seq BE, uint8 opcode, payload + const seq = buf.readUInt16BE(0); + const op = buf.readUInt8(2); + const payload = buf.subarray(3); + lastSeq = seq; + onDaveBinary(op, payload, seq); + return; + } + const p = JSON.parse(raw.toString()); + handleVoiceJson(p); + }); + voiceWs.on('close', (c, r) => { + evidence.voiceCloseCode = c; evidence.voiceCloseReason = r?.toString() || ''; + log(`voice gw: CLOSE code=${c} reason="${evidence.voiceCloseReason}"`); + clearInterval(voiceHb); + if (c === 4017) finish('FAIL_4017', 'voice gateway rejected with close 4017 (DAVE-unsupported/protocol)'); + else if (!finished) finish(evidence.voiceReady ? evidence.verdict : 'FAIL_VOICE_CLOSED', `voice closed ${c}`); + }); + voiceWs.on('error', (e) => log('voice gw error', e.message)); + + function handleVoiceJson(p) { + switch (p.op) { + case 8: { // HELLO + const iv = p.d.heartbeat_interval; + voiceHb = setInterval(() => { + try { voiceWs.send(JSON.stringify({ op: 3, d: { t: Date.now(), seq_ack: lastSeq ?? 0 } })); } catch {} + }, iv); + log(`voice gw: HELLO heartbeat_interval=${iv}`); + break; + } + case 2: { // READY + evidence.voiceReady = true; + evidence.voiceReadyModes = p.d.modes; + voiceState.ssrc = p.d.ssrc; voiceState.ip = p.d.ip; voiceState.port = p.d.port; + log(`voice gw: READY ssrc=${p.d.ssrc} udp=${p.d.ip}:${p.d.port} modes=${JSON.stringify(p.d.modes)}`); + doUdpDiscoveryAndSelect(p.d); + break; + } + case 4: { // Session Description / select_protocol_ack (carries dave_protocol_version) + if (p.d && p.d.dave_protocol_version != null) { + evidence.negotiatedDaveVersion = p.d.dave_protocol_version; + log(`voice gw: SESSION_DESCRIPTION dave_protocol_version=${p.d.dave_protocol_version} mode=${p.d.mode}`); + } else { + log(`voice gw: SESSION_DESCRIPTION (no dave version field) mode=${p.d?.mode}`); + } + evaluateGate(); + break; + } + case 11: { // clients connect + for (const u of (p.d.user_ids || [])) knownUsers.add(u); + log(`voice gw: op11 clients_connect ${JSON.stringify(p.d.user_ids)}`); + break; + } + case 20: { if (p.d.user_id) knownUsers.add(p.d.user_id); log(`voice gw: op20 platform user=${p.d.user_id}`); break; } + case 21: { // dave_prepare_transition (JSON) + evidence.sawDaveOpcodes.push(21); + log(`voice gw: DAVE op21 prepare_transition ${JSON.stringify(p.d)}`); + // ack readiness + try { voiceWs.send(JSON.stringify({ op: 23, d: { transition_id: p.d.transition_id } })); } catch {} + break; + } + case 22: { + evidence.sawDaveOpcodes.push(22); + log(`voice gw: DAVE op22 execute_transition ${JSON.stringify(p.d)}`); + if (daveSession && daveSession.ready) { evidence.mlsSessionReady = true; } + evaluateGate(); + break; + } + case 24: { + evidence.sawDaveOpcodes.push(24); + log(`voice gw: DAVE op24 prepare_epoch ${JSON.stringify(p.d)}`); + break; + } + case 31: { + evidence.sawDaveOpcodes.push(31); + log(`voice gw: DAVE op31 invalid_commit_welcome ${JSON.stringify(p.d)}`); + break; + } + default: + log(`voice gw: op${p.op} ${JSON.stringify(p.d)?.slice(0, 200)}`); + } + } + + function onDaveBinary(op, payload, seq) { + evidence.sawDaveOpcodes.push(op); + log(`voice gw: DAVE binary op${op} seq=${seq} len=${payload.length}`); + try { + switch (op) { + case 25: { // external sender package + evidence.sawExternalSender = true; + if (!daveSession) { + daveSession = new davey.DAVESession(evidence.negotiatedDaveVersion || DAVE_VER, '1513862586112671786', CHANNEL_ID); + } + daveSession.setExternalSender(payload); + const kp = daveSession.getSerializedKeyPackage(); + // send op26 key package (binary): [uint8 opcode][payload] + voiceWs.send(Buffer.concat([Buffer.from([26]), kp]), { binary: true }); + log(`voice gw: sent DAVE op26 key_package len=${kp.length}`); + break; + } + case 27: { // proposals: [operation_type u8][proposals...] + const opType = payload.readUInt8(0); // ProposalsOperationType + const proposals = payload.subarray(1); + const known = Array.from(knownUsers); + const res = daveSession.processProposals(opType, proposals, known); + if (res && res.commit) { + const parts = [Buffer.from([28]), res.commit]; + if (res.welcome) parts.push(res.welcome); + voiceWs.send(Buffer.concat(parts), { binary: true }); + log(`voice gw: sent DAVE op28 commit_welcome commit=${res.commit.length} welcome=${res.welcome?.length || 0} (known=${known.length})`); + } else { + log(`voice gw: op27 processed, no commit produced (known=${known.length})`); + } + break; + } + case 29: { // announce commit transition: [transition_id u16][commit...] + const commit = payload.subarray(2); + daveSession.processCommit(commit); + if (daveSession.ready) evidence.mlsSessionReady = true; + log(`voice gw: processed op29 commit (tid=${payload.readUInt16BE(0)}), sessionReady=${daveSession.ready}`); + evaluateGate(); + break; + } + case 30: { // welcome: [transition_id u16][welcome...] + const welcome = payload.subarray(2); + daveSession.processWelcome(welcome); + if (daveSession.ready) evidence.mlsSessionReady = true; + log(`voice gw: processed op30 welcome (tid=${payload.readUInt16BE(0)}), sessionReady=${daveSession.ready}`); + evaluateGate(); + break; + } + default: + log(`voice gw: unhandled DAVE binary op${op}`); + } + } catch (e) { + evidence.notes.push(`DAVE op${op} error: ${e.message}`); + log(`voice gw: DAVE op${op} handler error: ${e.message}`); + } + } + + function daveKnownUsers() { return ['1513862586112671786']; } + + function doUdpDiscoveryAndSelect(ready) { + udp = dgram.createSocket('udp4'); + const disc = Buffer.alloc(74); + disc.writeUInt16BE(1, 0); disc.writeUInt16BE(70, 2); disc.writeUInt32BE(ready.ssrc, 4); + let selected = false; + udp.on('message', (msg) => { + if (selected) return; selected = true; + const ipEnd = msg.indexOf(0, 8); + const ip = msg.subarray(8, ipEnd).toString(); + const port = msg.readUInt16BE(msg.length - 2); + const mode = (ready.modes || []).includes('aead_aes256_gcm_rtpsize') ? 'aead_aes256_gcm_rtpsize' + : (ready.modes || []).includes('aead_xchacha20_poly1305_rtpsize') ? 'aead_xchacha20_poly1305_rtpsize' + : (ready.modes || [])[0]; + voiceState.mode = mode; + voiceWs.send(JSON.stringify({ op: 1, d: { protocol: 'udp', data: { address: ip, port, mode }, codecs: [ + { name: 'opus', type: 'audio', priority: 1000, payload_type: 120 }, + { name: 'VP8', type: 'video', priority: 1000, payload_type: 101, rtx_payload_type: 102 }, + { name: 'H264', type: 'video', priority: 2000, payload_type: 103, rtx_payload_type: 104 }, + ] }})); + log(`voice gw: sent SELECT PROTOCOL (mode=${mode}) after UDP discovery ${ip}:${port}`); + }); + udp.on('error', (e) => log('udp error', e.message)); + udp.send(disc, ready.port, ready.ip, (e) => { if (e) log('udp send err', e.message); else log('udp: sent IP discovery'); }); + } + + function evaluateGate() { + if (finished) return; + if (!evidence.voiceReady || evidence.negotiatedDaveVersion == null) return; + if (evidence.negotiatedDaveVersion === 0) { + // DAVE not enforced on this channel right now: gateway accepted, no E2EE membership needed. + return finish('PASS_NO_DAVE', 'voice gateway accepted; dave_protocol_version=0 (E2EE not active on this channel)'); + } + if (evidence.mlsSessionReady) { + // Full E2EE membership achieved. Hold briefly to confirm the membership stays stable + // (no server disconnect), then leave cleanly. + log('gate: MLS session READY — holding 5s to confirm stable membership'); + setTimeout(() => { + if (evidence.voiceCloseCode == null) evidence.notes.push('membership stable for 5s (no server disconnect)'); + finish('PASS_MLS_READY', `DAVE v${evidence.negotiatedDaveVersion} negotiated; joined E2EE MLS group (session ready); privacyCode=${daveSession?.voicePrivacyCode || 'n/a'}`); + }, 5000); + } + // else: DAVE negotiated but MLS not yet complete — keep waiting for op25/27/29/30. + } +} diff --git a/dave/package-lock.json b/dave/package-lock.json new file mode 100644 index 0000000..737cd13 --- /dev/null +++ b/dave/package-lock.json @@ -0,0 +1,337 @@ +{ + "name": "wsai-dave-poc", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wsai-dave-poc", + "dependencies": { + "@snazzah/davey": "^0.1.12", + "ws": "^8.18.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, + "node_modules/@snazzah/davey": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey/-/davey-0.1.12.tgz", + "integrity": "sha512-V+NlX5931RwVamZhhEfZekMdcvXDKdMAmHW1AuGaykVQsNyBOq3bpmGpoKRBDCYgFWKIufJ0Dcg3m4cYhvUy6g==", + "engines": { + "node": ">= 10" + }, + "funding": { + "url": "https://github.com/sponsors/Snazzah" + }, + "optionalDependencies": { + "@snazzah/davey-android-arm-eabi": "0.1.12", + "@snazzah/davey-android-arm64": "0.1.12", + "@snazzah/davey-darwin-arm64": "0.1.12", + "@snazzah/davey-darwin-x64": "0.1.12", + "@snazzah/davey-freebsd-x64": "0.1.12", + "@snazzah/davey-linux-arm-gnueabihf": "0.1.12", + "@snazzah/davey-linux-arm64-gnu": "0.1.12", + "@snazzah/davey-linux-arm64-musl": "0.1.12", + "@snazzah/davey-linux-x64-gnu": "0.1.12", + "@snazzah/davey-linux-x64-musl": "0.1.12", + "@snazzah/davey-wasm32-wasi": "0.1.12", + "@snazzah/davey-win32-arm64-msvc": "0.1.12", + "@snazzah/davey-win32-ia32-msvc": "0.1.12", + "@snazzah/davey-win32-x64-msvc": "0.1.12" + } + }, + "node_modules/@snazzah/davey-android-arm-eabi": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-android-arm-eabi/-/davey-android-arm-eabi-0.1.12.tgz", + "integrity": "sha512-6VC/an+Sx5dI5skb+90rYcIB1jhm48Rl0nDaw0UNT4bz1rMjpVfmmZqeocYXMq96IdbBMlE6OTKGcBm2C3gkQg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-android-arm64": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-android-arm64/-/davey-android-arm64-0.1.12.tgz", + "integrity": "sha512-0Bwd03/JsTFhlPhF4q/LW0RxSzntFpQdhz+TBdFljYSg8IEyA38saPJeTNjpIgDfhAumPzvhCdfS6O5qT8yXDw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-darwin-arm64": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-darwin-arm64/-/davey-darwin-arm64-0.1.12.tgz", + "integrity": "sha512-lKMV6ITi9BQLt0fx/pAT7M8xcojVK7bryVJGdaW3bq8gABFslS3ti/KzrWabQvhpEV71FZe5mV0UcKHFVaTsZw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-darwin-x64": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-darwin-x64/-/davey-darwin-x64-0.1.12.tgz", + "integrity": "sha512-vXXc/eW/e3TQeb7VsdtrPqs3/22j0aSqiP1ZXmZtDjQRBwgSxwItWYa6sh5MELP2EHB2igNlGzB6Hc0XlbGi4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-freebsd-x64": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-freebsd-x64/-/davey-freebsd-x64-0.1.12.tgz", + "integrity": "sha512-G1gas5HrC4Xp3mRY0+OeAqXS6fG2tRgBEc8gQh69Hw4YK9RV9mzQKcmoKMkBM72U1+2C+2u57dolnKKzwozByQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-linux-arm-gnueabihf": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-arm-gnueabihf/-/davey-linux-arm-gnueabihf-0.1.12.tgz", + "integrity": "sha512-97Fujh82r2Ll7dPZeNuoZ3yKfsqycf3c93OWXOo/ThNL/18Onl2Ht4SIvpX6VHhfeS9bDpHJ1lHCaz1b/i5ocw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-linux-arm64-gnu": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-arm64-gnu/-/davey-linux-arm64-gnu-0.1.12.tgz", + "integrity": "sha512-FWyAOv52cHKDM4BOsmImKKogHFvqNFoXmZcicNJbX3XpVl8Mas88ZoXQ+IA5V+qc9pNtAl1MbWwEZ9JrqAQtbg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-linux-arm64-musl": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-arm64-musl/-/davey-linux-arm64-musl-0.1.12.tgz", + "integrity": "sha512-ptRbLSQxtV6EjXppS5z7qaPDI0NRKhrkJYsTlAjEghmOvlAObozSCYYnMO6nbkt6Ab3+lWqyahClzcRNcD2ouw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-linux-x64-gnu": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-x64-gnu/-/davey-linux-x64-gnu-0.1.12.tgz", + "integrity": "sha512-w86fZvhJn0ErOoAQHt2UbQ95V/cgwvfvQ4GlTPQLCzt58nn+rLlXLgPn90qYlSQrZxFW38rKXwqVOMbm9p+pwQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-linux-x64-musl": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-x64-musl/-/davey-linux-x64-musl-0.1.12.tgz", + "integrity": "sha512-LLNnO+hfG41ymeI+O1YHo5/0h3aKaetUNLdkBwpdJsjoyKMXZaeCnB+aHNkkupJCMPmWS0g6iPMCHUOqZSBcTg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-wasm32-wasi": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-wasm32-wasi/-/davey-wasm32-wasi-0.1.12.tgz", + "integrity": "sha512-MPKFuqYVkDFXheR7qmtEY4FWxQ/ADfgsCojQWHi13sibUqCTR9q2F1LqNn2i9IVh3sh1sxeg87fdFMCH63pl7g==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@snazzah/davey-win32-arm64-msvc": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-win32-arm64-msvc/-/davey-win32-arm64-msvc-0.1.12.tgz", + "integrity": "sha512-Uf4OYHyfbXpzyaOqIV8/h6kv166Qni5+Bxmc1E/ov4uhhKO8qXbOky8zbVOzu0U4cv5ll3s4IQ8jDAJkx/K75Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-win32-ia32-msvc": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-win32-ia32-msvc/-/davey-win32-ia32-msvc-0.1.12.tgz", + "integrity": "sha512-nRVbKTsb2ldcPI8D4BDA7P/UeiMEMvR+wYuUMp7H1pRD/3dF2hKo+MzUU+Pn78EhuYA3CWHItiAcS/GsPld67A==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@snazzah/davey-win32-x64-msvc": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@snazzah/davey-win32-x64-msvc/-/davey-win32-x64-msvc-0.1.12.tgz", + "integrity": "sha512-AgUA3itPDVkxQq7RIkgE1thCiWePwWjyfOZefBBxGIlMWpRUOSwF4vY9kNLnrScyJcFULdR+Zm8PwiwV8/RKnw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true + }, + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/dave/package.json b/dave/package.json new file mode 100644 index 0000000..5686855 --- /dev/null +++ b/dave/package.json @@ -0,0 +1,10 @@ +{ + "name": "wsai-dave-poc", + "private": true, + "type": "module", + "description": "Fail-fast gate: can a selfbot pass the Discord voice DAVE/MLS (close 4017) handshake?", + "dependencies": { + "ws": "^8.18.0", + "@snazzah/davey": "^0.1.12" + } +}