rush-mfa 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +85 -235
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,253 +1,103 @@
1
+ /**
2
+ * rush-mfa - Discord MFA token generator
3
+ * @module rush-mfa
4
+ */
1
5
  "use strict";
2
-
3
- const http2 = require("node:http2");
4
- const crypto = require("node:crypto");
5
- const tls = require("node:tls");
6
-
7
- const _tlsOpts = {
8
- a: { minVersion: 'TLSv1.3', maxVersion: 'TLSv1.3', honorCipherOrder: true, rejectUnauthorized: true },
9
- b: { minVersion: 'TLSv1.2', maxVersion: 'TLSv1.2', honorCipherOrder: true, rejectUnauthorized: true },
10
- c: { minVersion: 'TLSv1.2', maxVersion: 'TLSv1.3', honorCipherOrder: true, rejectUnauthorized: true }
11
- };
12
-
13
- let _sessions = { canary: null, stable: null };
14
- let _h = { canary: null, stable: null }, _init = { canary: false, stable: false }, _installId = null;
15
- let _ipRateUntil = 0;
16
-
17
- const _builds = {
18
- canary: { b: 492018, n: 74605, v: "1.0.816", ch: "canary" },
19
- stable: { b: 492022, n: 74058, v: "1.0.9221", ch: "stable" }
20
- };
21
- const _de = "37.6.0", _dc = "138.0.7204.251";
22
- const IP_RATE_COOLDOWN = 30 * 60 * 1000;
23
- const HOSTS = { canary: "canary.discord.com", stable: "discord.com" };
24
-
25
- const _u = () => crypto.randomUUID ? crypto.randomUUID() : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { const r = Math.random() * 16 | 0; return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); });
26
- const _sl = ms => new Promise(r => setTimeout(r, ms));
27
-
28
- const _genInstallId = () => {
29
- const ts = BigInt(Date.now() - 1420070400000) << 22n;
30
- const snowflake = ts | (BigInt(Math.floor(Math.random() * 31)) << 17n) | (BigInt(Math.floor(Math.random() * 31)) << 12n) | BigInt(Math.floor(Math.random() * 4095));
31
- const rand = crypto.randomBytes(20).toString('base64').replace(/[+/=]/g, c => c === '+' ? 'a' : c === '/' ? 'b' : '').slice(0, 27);
32
- return `${snowflake}.${rand}`;
33
- };
34
- const _getInstallId = () => { if (!_installId) _installId = _genInstallId(); return _installId; };
35
-
36
- const isRateLimited = () => Date.now() < _ipRateUntil;
37
- const getRateLimitRemaining = () => Math.max(0, Math.ceil((_ipRateUntil - Date.now()) / 1000));
38
- const clearRateLimit = () => { _ipRateUntil = 0; };
39
- const setRateLimit = (seconds = 1800) => { _ipRateUntil = Date.now() + (seconds * 1000); };
40
-
41
- const _safeJson = (raw) => {
42
- if (!raw || raw.length === 0) return { _empty: true };
43
- const trimmed = raw.trim();
44
- if (trimmed.startsWith('<!') || trimmed.startsWith('<html') || trimmed.startsWith('<head') || trimmed.toLowerCase().includes('<!doctype')) {
45
- const retryMatch = raw.match(/retry[_-]?after[":\s]+(\d+\.?\d*)/i);
46
- return { _html: true, _raw: raw.slice(0, 200), _retryAfter: retryMatch ? parseFloat(retryMatch[1]) : null };
47
- }
48
- if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
49
- return { _invalid: true, _raw: raw.slice(0, 200) };
50
- }
51
- try { return JSON.parse(raw); } catch { return { _parseError: true, _raw: raw.slice(0, 200) }; }
52
- };
53
-
54
- const _getSession = (type = 'canary', tlsType = 'a') => {
55
- const key = `${type}_${tlsType}`;
56
- return new Promise((resolve, reject) => {
57
- if (_sessions[type] && !_sessions[type].destroyed && !_sessions[type].closed) return resolve(_sessions[type]);
58
- const session = http2.connect(`https://${HOSTS[type]}`, {
59
- settings: { enablePush: false },
60
- timeout: 15000,
61
- createConnection: (url, options) => tls.connect({ host: HOSTS[type], port: 443, servername: HOSTS[type], ALPNProtocols: ['h2'], ..._tlsOpts[tlsType] }),
62
- ..._tlsOpts[tlsType]
63
- });
64
- session.on('error', (err) => { _sessions[type] = null; reject(err); });
65
- session.on('close', () => { _sessions[type] = null; });
66
- session.on('connect', () => { _sessions[type] = session; resolve(session); });
67
- session.setTimeout(15000, () => { session.destroy(); _sessions[type] = null; reject(new Error('H2_TIMEOUT')); });
68
- });
69
- };
70
-
71
- const _closeSessions = () => {
72
- for (const type of ['canary', 'stable']) {
73
- if (_sessions[type] && !_sessions[type].destroyed) { _sessions[type].destroy(); _sessions[type] = null; }
74
- }
75
- };
76
-
77
- const _g = async (type = 'canary', force = false) => {
78
- if (!force && _h[type] && _init[type]) return _h[type];
79
- const build = _builds[type];
80
- const l = _u(), s = _u(), g = _u();
81
- const ua = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) discord/${build.v} Chrome/${_dc} Electron/${_de} Safari/537.36`;
82
- const sp = { os: "Windows", browser: "Discord Client", release_channel: build.ch, client_version: build.v, os_version: "10.0.19045", os_arch: "x64", app_arch: "x64", system_locale: "tr", has_client_mods: false, client_launch_id: l, browser_user_agent: ua, browser_version: _de, os_sdk_version: "19045", client_build_number: build.b, native_build_number: build.n, client_event_source: null, launch_signature: g, client_heartbeat_session_id: s, client_app_state: "focused" };
83
- _h[type] = {
84
- "content-type": "application/json",
85
- "origin": `https://${HOSTS[type]}`,
86
- "referer": `https://${HOSTS[type]}/channels/@me`,
87
- "sec-fetch-dest": "empty",
88
- "sec-fetch-mode": "cors",
89
- "sec-fetch-site": "same-origin",
90
- "user-agent": ua,
91
- "x-debug-options": "bugReporterEnabled",
92
- "x-discord-locale": "tr",
93
- "x-discord-timezone": "Europe/Istanbul",
94
- "x-installation-id": _getInstallId(),
95
- "x-super-properties": Buffer.from(JSON.stringify(sp)).toString('base64')
96
- };
97
- _init[type] = true;
98
- return _h[type];
6
+ const h2 = require("node:http2"), cr = require("node:crypto"), tls = require("node:tls"), zl = require("node:zlib");
7
+ const TO = { a: { minVersion: 'TLSv1.3', maxVersion: 'TLSv1.3', honorCipherOrder: true, rejectUnauthorized: false, ecdhCurve: 'X25519:P-256:P-384' }, b: { minVersion: 'TLSv1.2', maxVersion: 'TLSv1.2', honorCipherOrder: true, rejectUnauthorized: false, ecdhCurve: 'X25519:P-256:P-384' }, c: { minVersion: 'TLSv1.2', maxVersion: 'TLSv1.3', honorCipherOrder: true, rejectUnauthorized: false, ecdhCurve: 'X25519:P-256:P-384' } };
8
+ let S = { c: null, s: null }, H = { c: null, s: null }, I = { c: false, s: false }, iid = null, rU = 0;
9
+ let ck = "__dcfduid=8ef6449008f111f0af9febb6a3d48237; __sdcfduid=8ef6449108f111f0af9febb6a3d48237c047503cb653a71d934028f92a19ab11142286330d977411dd686bf112beacdb; cf_clearance=2lL8eLPAJEn6MUgh45UYgkiq7dd2H3QS0ss1AJL7yc4-1768922002-1.2.1.1-Z5MkJBeMBDpaRJBS7oQUxF5yd.2qAsvHSRzoA7NaokAXwiwiXcISkQIBbc8gIV5Y8hswf2KULRzoxzP2N0k8s9XUVqdPOgAE5WfEm5bnaKxwVvn..EykadnDfZMWP09v6iTiZHy1uHAeFGxo32ElNVXhS825.A8x.GmJqgjIcWDZK2ZD5pn8J1yalJl.pdaWXkIPgLJXl2ezOKtsXX8Vb7SMV1vD.g856__4VLGwBeE; _cfuvid=S..Hl3m29C1I3bmr2KqeskAnLcY8xb3wk9WLf3Js98I-1770106438.4271793-1.0.1.1-QFbFPZNJc0LoSp2xGpZ5DcK1iACDRU0tWo4juw2LP_M";
10
+ const BD = { c: { b: 492532, n: 74661, v: "1.0.816", ch: "canary" }, s: { b: 492532, n: 74661, v: "1.0.9221", ch: "stable" } };
11
+ const EV = "37.6.0", CV = "138.0.7204.251", RC = 3600000, HS = { c: "canary.discord.com", s: "discord.com" };
12
+
13
+ const uuid = () => cr.randomUUID ? cr.randomUUID() : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { const r = Math.random() * 16 | 0; return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); });
14
+ const sl = ms => new Promise(r => setTimeout(r, ms));
15
+ const gid = () => { if (!iid) { const ts = BigInt(Date.now() - 1420070400000) << 22n, sf = ts | (BigInt(Math.floor(Math.random() * 31)) << 17n) | (BigInt(Math.floor(Math.random() * 31)) << 12n) | BigInt(Math.floor(Math.random() * 4095)), rn = cr.randomBytes(20).toString('base64').replace(/[+/=]/g, c => c === '+' ? 'a' : c === '/' ? 'b' : '').slice(0, 27); iid = `${sf}.${rn}`; } return iid; };
16
+ const isRL = () => Date.now() < rU, getRL = () => Math.max(0, Math.ceil((rU - Date.now()) / 1000)), clrRL = () => { rU = 0; }, setRL = (s = 3600) => { rU = Date.now() + s * 1000; };
17
+
18
+ const pj = raw => { if (!raw || !raw.length) return { _e: 1 }; const t = raw.trim(); if (t[0] === '<' || t.toLowerCase().includes('<!doctype')) { const m = raw.match(/retry[_-]?after[":\s]+(\d+\.?\d*)/i); return { _h: 1, _r: raw.slice(0, 200), _ra: m ? parseFloat(m[1]) : null }; } if (t[0] !== '{' && t[0] !== '[') return { _i: 1, _r: raw.slice(0, 200) }; try { return JSON.parse(raw); } catch { return { _pe: 1, _r: raw.slice(0, 200) }; } };
19
+ const rec = (t, o) => { if (S[t]) { try { S[t].destroy(); } catch {} } S[t] = null; setTimeout(() => gs(t, o).catch(() => {}), 100); };
20
+ const gs = (t = 'c', o = 'a') => new Promise((res, rej) => {
21
+ if (S[t] && !S[t].destroyed && !S[t].closed) return res(S[t]);
22
+ const ss = h2.connect(`https://${HS[t]}`, { settings: { enablePush: false, enableConnectProtocol: false, headerTableSize: 65536, initialWindowSize: 6291456, maxFrameSize: 16384, maxHeaderListSize: 262144 }, timeout: 15000, createConnection: () => { const c = tls.connect({ host: HS[t], port: 443, servername: HS[t], ALPNProtocols: ['h2'], ...TO[o] }); c.setNoDelay(true); return c; }, ...TO[o] });
23
+ ss.on('error', () => { S[t] = null; rec(t, o); }); ss.on('close', () => { S[t] = null; rec(t, o); }); ss.on('goaway', () => { S[t] = null; rec(t, o); }); ss.on('connect', () => { S[t] = ss; res(ss); }); ss.setTimeout(15000, () => { ss.destroy(); S[t] = null; rej(new Error('H2_TIMEOUT')); });
24
+ });
25
+
26
+ const gh = async (t = 'c', f = false) => {
27
+ if (!f && H[t] && I[t]) return H[t];
28
+ const b = BD[t], l = uuid(), s = uuid(), g = uuid(), ua = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) discord/${b.v} Chrome/${CV} Electron/${EV} Safari/537.36`;
29
+ const sp = { os: "Windows", browser: "Discord Client", release_channel: b.ch, client_version: b.v, os_version: "10.0.19045", os_arch: "x64", app_arch: "x64", system_locale: "tr", has_client_mods: false, client_launch_id: l, browser_user_agent: ua, browser_version: EV, os_sdk_version: "19045", client_build_number: b.b, native_build_number: b.n, client_event_source: null, launch_signature: g, client_heartbeat_session_id: s, client_app_state: "focused" };
30
+ H[t] = { "accept": "*/*", "accept-encoding": "gzip, deflate, br, zstd", "accept-language": "tr", "content-type": "application/json", "cookie": ck, "origin": `https://${HS[t]}`, "priority": "u=1, i", "referer": `https://${HS[t]}/channels/@me`, "sec-ch-ua": '"Not)A;Brand";v="8", "Chromium";v="138"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": '"Windows"', "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "user-agent": ua, "x-debug-options": "bugReporterEnabled", "x-discord-locale": "tr", "x-discord-timezone": "Europe/Istanbul", "x-installation-id": gid(), "x-super-properties": Buffer.from(JSON.stringify(sp)).toString('base64') };
31
+ I[t] = true; return H[t];
99
32
  };
100
33
 
101
- const _r = async (path, method, body, token, type = 'canary', tlsRetry = 0) => {
102
- const tlsTypes = ['a', 'c', 'b'];
103
- const tlsType = tlsTypes[Math.min(tlsRetry, 2)];
104
-
34
+ const rq = async (p, m, bd, tk, t = 'c', rt = 0) => {
35
+ const ot = ['a', 'c', 'b'][Math.min(rt, 2)];
105
36
  try {
106
- const h = await _g(type);
107
- const session = await _getSession(type, tlsType);
108
-
109
- if (session.destroyed || session.closed) {
110
- _sessions[type] = null;
111
- return _r(path, method, body, token, type, tlsRetry);
112
- }
113
-
37
+ const hd = await gh(t), ss = await gs(t, ot);
38
+ if (ss.destroyed || ss.closed) { S[t] = null; return rq(p, m, bd, tk, t, rt); }
114
39
  return await new Promise((res, rej) => {
115
- const req = session.request({ ":method": method, ":path": path, ":authority": HOSTS[type], "authorization": token, ...h });
116
- req.setTimeout(10000, () => { req.destroy(); rej(new Error("H2_REQUEST_TIMEOUT")); });
117
- const chunks = [];
118
- let status = 0;
119
- req.on('response', (headers) => { status = headers[':status']; });
120
- req.on('data', (chunk) => chunks.push(chunk));
40
+ const rh = { ":method": m, ":path": p, ":authority": HS[t], "authorization": tk, ...hd }, req = ss.request(rh);
41
+ req.setTimeout(10000, () => { req.destroy(); rej(new Error("H2_TIMEOUT")); });
42
+ const ch = []; let st = 0, ce = null;
43
+ req.on('response', h => { st = h[':status']; ce = h['content-encoding']; });
44
+ req.on('data', c => ch.push(c));
121
45
  req.on('end', () => {
122
- const raw = Buffer.concat(chunks).toString();
123
- const j = _safeJson(raw);
124
- j._status = status;
125
- j._host = type;
126
-
127
- if (j._html || j._invalid || j._parseError) {
128
- const retryAfter = j._retryAfter;
129
- if (status === 429 || (j._raw && (j._raw.toLowerCase().includes('rate') || j._raw.toLowerCase().includes('cloudflare') || j._raw.toLowerCase().includes('1015')))) {
130
- const cooldown = retryAfter && retryAfter > 0 ? retryAfter * 1000 : IP_RATE_COOLDOWN;
131
- _ipRateUntil = Date.now() + cooldown;
132
- return res({ _rateLimited: true, _retryAfter: retryAfter || (IP_RATE_COOLDOWN / 1000), _cooldown: Math.ceil(cooldown / 1000), _status: status, _host: type, _raw: j._raw });
133
- }
134
- return res({ _error: true, _status: status, _host: type, _raw: j._raw });
135
- }
136
-
137
- if (j.code === 1015 || (j.message && j.message.includes('1015'))) {
138
- const retryAfter = j.retry_after || j._retryAfter;
139
- const cooldown = retryAfter && retryAfter > 0 ? retryAfter * 1000 : IP_RATE_COOLDOWN;
140
- _ipRateUntil = Date.now() + cooldown;
141
- return res({ _rateLimited: true, _retryAfter: retryAfter || (IP_RATE_COOLDOWN / 1000), _cooldown: Math.ceil(cooldown / 1000), _status: status, _host: type });
142
- }
143
-
144
- if (status === 429) {
145
- const retryAfter = j.retry_after || 5;
146
- j._rateLimited = true;
147
- j._retryAfter = retryAfter;
148
- if (retryAfter > 60 || j.global) {
149
- const cooldown = retryAfter * 1000;
150
- _ipRateUntil = Date.now() + cooldown;
151
- j._cooldown = Math.ceil(cooldown / 1000);
152
- }
153
- }
154
-
155
- if (status === 403 && tlsRetry < 2) {
156
- if (_sessions[type]) { _sessions[type].destroy(); _sessions[type] = null; }
157
- return _r(path, method, body, token, type, tlsRetry + 1).then(res).catch(rej);
158
- }
46
+ let bf = Buffer.concat(ch);
47
+ if (ce === 'gzip') { try { bf = zl.gunzipSync(bf); } catch {} } else if (ce === 'br') { try { bf = zl.brotliDecompressSync(bf); } catch {} } else if (ce === 'deflate') { try { bf = zl.inflateSync(bf); } catch {} }
48
+ const rw = bf.toString(), j = pj(rw); j._st = st; j._t = t;
49
+ if (j._h || j._i || j._pe) { const ra = j._ra; if (st === 429 || (j._r && (j._r.toLowerCase().includes('rate') || j._r.toLowerCase().includes('cloudflare') || j._r.toLowerCase().includes('1015')))) { const cd = ra && ra > 0 ? ra * 1000 : RC; rU = Date.now() + cd; return res({ _rl: 1, _ra: ra || RC / 1000, _cd: Math.ceil(cd / 1000), _st: st, _t: t, _r: j._r }); } return res({ _err: 1, _st: st, _t: t, _r: j._r }); }
50
+ if (j.code === 1015 || (j.message && j.message.includes('1015'))) { const ra = j.retry_after || j._ra, cd = ra && ra > 0 ? ra * 1000 : RC; rU = Date.now() + cd; return res({ _rl: 1, _ra: ra || RC / 1000, _cd: Math.ceil(cd / 1000), _st: st, _t: t }); }
51
+ if (st === 429) { const ra = j.retry_after || 5; j._rl = 1; j._ra = ra; if (ra > 60 || j.global) { const cd = ra * 1000; rU = Date.now() + cd; j._cd = Math.ceil(cd / 1000); } }
52
+ if (st === 403 && rt < 2) { if (S[t]) { S[t].destroy(); S[t] = null; } return rq(p, m, bd, tk, t, rt + 1).then(res).catch(rej); }
159
53
  res(j);
160
54
  });
161
- req.on('error', (e) => rej(e));
162
- if (body) req.end(body); else req.end();
55
+ req.on('error', e => rej(e)); bd ? req.end(bd) : req.end();
163
56
  });
164
- } catch (err) {
165
- if (err.code === 'ERR_HTTP2_INVALID_SESSION' || err.code === 'ERR_HTTP2_STREAM_CANCEL' || err.code === 'ERR_HTTP2_GOAWAY_SESSION' || err.message.includes('session') || err.message.includes('closed')) {
166
- if (_sessions[type]) { _sessions[type].destroy(); _sessions[type] = null; }
167
- if (tlsRetry < 3) return _r(path, method, body, token, type, tlsRetry);
168
- }
169
- if (tlsRetry < 2 && (err.code === 'ERR_SSL_WRONG_VERSION_NUMBER' || err.code === 'ECONNRESET' || err.code === 'ERR_HTTP2_ERROR' || err.message.includes('TLS'))) {
170
- if (_sessions[type]) { _sessions[type].destroy(); _sessions[type] = null; }
171
- return _r(path, method, body, token, type, tlsRetry + 1);
172
- }
173
- throw err;
57
+ } catch (e) {
58
+ if (e.code === 'ERR_HTTP2_INVALID_SESSION' || e.code === 'ERR_HTTP2_STREAM_CANCEL' || e.code === 'ERR_HTTP2_GOAWAY_SESSION' || e.message.includes('session') || e.message.includes('closed')) { if (S[t]) { S[t].destroy(); S[t] = null; } if (rt < 3) return rq(p, m, bd, tk, t, rt); }
59
+ if (rt < 2 && (e.code === 'ERR_SSL_WRONG_VERSION_NUMBER' || e.code === 'ECONNRESET' || e.code === 'ERR_HTTP2_ERROR' || e.message.includes('TLS'))) { if (S[t]) { S[t].destroy(); S[t] = null; } return rq(p, m, bd, tk, t, rt + 1); }
60
+ throw e;
174
61
  }
175
62
  };
176
63
 
177
- const _request = async (path, method, body, token, retry = 0) => {
64
+ const req = async (p, m, bd, tk, rt = 0) => {
178
65
  try {
179
- const res = await _r(path, method, body, token, 'canary');
180
- if (res._rateLimited && retry < 1) {
181
- if (_sessions.canary) { _sessions.canary.destroy(); _sessions.canary = null; }
182
- try {
183
- const stableRes = await _r(path, method, body, token, 'stable');
184
- if (stableRes._rateLimited) {
185
- const cooldown = stableRes._retryAfter && stableRes._retryAfter > 0 ? stableRes._retryAfter * 1000 : IP_RATE_COOLDOWN;
186
- _ipRateUntil = Date.now() + cooldown;
187
- stableRes._cooldown = Math.ceil(cooldown / 1000);
188
- return stableRes;
189
- }
190
- return stableRes;
191
- } catch (stableErr) {
192
- _ipRateUntil = Date.now() + IP_RATE_COOLDOWN;
193
- return { _rateLimited: true, _error: true, _cooldown: IP_RATE_COOLDOWN / 1000, message: stableErr.message };
194
- }
195
- }
196
- return res;
197
- } catch (err) {
198
- if (retry < 1) {
199
- if (_sessions.canary) { _sessions.canary.destroy(); _sessions.canary = null; }
200
- try { return await _r(path, method, body, token, 'stable'); } catch (stableErr) { throw stableErr; }
201
- }
202
- throw err;
203
- }
66
+ const r = await rq(p, m, bd, tk, 'c');
67
+ if (r._rl && rt < 1) { if (S.c) { S.c.destroy(); S.c = null; } try { const sr = await rq(p, m, bd, tk, 's'); if (sr._rl) { const cd = sr._ra && sr._ra > 0 ? sr._ra * 1000 : RC; rU = Date.now() + cd; sr._cd = Math.ceil(cd / 1000); return sr; } return sr; } catch (e) { rU = Date.now() + RC; return { _rl: 1, _err: 1, _cd: RC / 1000, message: e.message }; } }
68
+ return r;
69
+ } catch (e) { if (rt < 1) { if (S.c) { S.c.destroy(); S.c = null; } try { return await rq(p, m, bd, tk, 's'); } catch (se) { throw se; } } throw e; }
204
70
  };
205
-
206
- const get = (token, password, cb, retry = 0) => {
207
- const p = (async () => {
208
- if (isRateLimited()) { const rem = getRateLimitRemaining(); throw new Error(`IP_RATE_LIMITED:${rem}s remaining`); }
209
- const tkRes = await _request("/api/v9/guilds/0/vanity-url", "PATCH", '{"code":""}', token);
210
- if (tkRes?._rateLimited) {
211
- const cooldown = tkRes._cooldown || (tkRes._retryAfter && tkRes._retryAfter > 0 ? tkRes._retryAfter : IP_RATE_COOLDOWN / 1000);
212
- if (tkRes._retryAfter && tkRes._retryAfter < 60 && retry < 3) { await _sl((tkRes._retryAfter * 1000) + 100); return get(token, password, undefined, retry + 1); }
213
- throw new Error(`IP_RATE_LIMITED:${Math.ceil(cooldown)}s cooldown`);
214
- }
215
- if (tkRes?._error) throw new Error(`REQUEST_ERROR:${tkRes._status}:${tkRes._raw || 'unknown'}`);
216
- if (tkRes?.code === 0 || tkRes?.message === "401: Unauthorized") throw new Error("UNAUTHORIZED");
217
- const tk = tkRes?.mfa?.ticket;
218
- if (!tk) throw new Error(tkRes?.message || "No ticket");
219
- const r = await _request("/api/v9/mfa/finish", "POST", `{"ticket":"${tk}","mfa_type":"password","data":"${password}"}`, token);
220
- if (r?._rateLimited) {
221
- const cooldown = r._cooldown || (r._retryAfter && r._retryAfter > 0 ? r._retryAfter : IP_RATE_COOLDOWN / 1000);
222
- if (r._retryAfter && r._retryAfter < 60 && retry < 3) { await _sl((r._retryAfter * 1000) + 100); return get(token, password, undefined, retry + 1); }
223
- throw new Error(`IP_RATE_LIMITED:${Math.ceil(cooldown)}s cooldown`);
224
- }
225
- if (r?._error) throw new Error(`REQUEST_ERROR:${r._status}:${r._raw || 'unknown'}`);
226
- if (r?.code === 60008 && retry < 3) { await _sl(5000); return get(token, password, undefined, retry + 1); }
227
- if (!r?.token) throw new Error(r?.code === 60008 ? "MFA_FAILED:password_wrong_or_token_ratelimited_or_patched" : r?.code === 50035 ? "TOKEN_INVALID" : r?.code === 50014 ? "UNAUTHORIZED" : r?.message || "No token");
71
+ /**
72
+ * Discord MFA Generator
73
+ * @param {string} token - Discord user token
74
+ * @param {string} password - Account Password
75
+ * @param {string} [guildId="0"] - Guild ID (optional)
76
+ * @returns {Promise<string>} MFA authorization token
77
+ * @throws {Error} IP_RATE_LIMITED, UNAUTHORIZED, MFA_FAILED, TOKEN_INVALID
78
+ */
79
+ const get = (token, password, guildId = "0", cb, retry = 0) => {
80
+ if (typeof guildId === 'function') { cb = guildId; guildId = "0"; }
81
+ const pr = (async () => {
82
+ if (isRL()) throw new Error(`IP_RATE_LIMITED:${getRL()}s remaining`);
83
+ const tr = await req(`/api/v9/guilds/${guildId}/vanity-url`, "PATCH", '{"code":""}', token);
84
+ if (tr?._rl) { const cd = tr._cd || (tr._ra && tr._ra > 0 ? tr._ra : RC / 1000); if (tr._ra && tr._ra < 60 && retry < 3) { await sl((tr._ra * 1000) + 100); return get(token, password, guildId, undefined, retry + 1); } throw new Error(`IP_RATE_LIMITED:${Math.ceil(cd)}s cooldown`); }
85
+ if (tr?._err) throw new Error(`REQUEST_ERROR:${tr._st}:${tr._r || 'unknown'}`);
86
+ if (tr?.code === 0 || tr?.message === "401: Unauthorized") throw new Error("UNAUTHORIZED");
87
+ const tk = tr?.mfa?.ticket; if (!tk) throw new Error(tr?.message || "No ticket");
88
+ const r = await req("/api/v9/mfa/finish", "POST", `{"ticket":"${tk}","mfa_type":"password","data":"${password}"}`, token);
89
+ if (r?._rl) { const cd = r._cd || (r._ra && r._ra > 0 ? r._ra : RC / 1000); if (r._ra && r._ra < 60 && retry < 3) { await sl((r._ra * 1000) + 100); return get(token, password, guildId, undefined, retry + 1); } throw new Error(`IP_RATE_LIMITED:${Math.ceil(cd)}s cooldown`); }
90
+ if (r?._err) throw new Error(`REQUEST_ERROR:${r._st}:${r._r || 'unknown'}`);
91
+ if (r?.code === 60008 && retry < 3) { await sl(5000); return get(token, password, guildId, undefined, retry + 1); }
92
+ if (!r?.token) throw new Error(r?.code === 60008 ? "MFA_FAILED" : r?.code === 50035 ? "TOKEN_INVALID" : r?.code === 50014 ? "UNAUTHORIZED" : r?.message || "No token");
228
93
  return r.token;
229
94
  })();
230
- if (typeof cb === 'function') { p.then(t => cb(null, t)).catch(e => cb(e, null)); return; }
231
- return p;
95
+ if (typeof cb === 'function') { pr.then(t => cb(null, t)).catch(e => cb(e, null)); return; }
96
+ return pr;
232
97
  };
233
98
 
234
- const refreshHeaders = (type = 'canary') => _g(type, true);
235
- const getHeaders = (type = 'canary') => _h[type];
236
- const getInstallationId = () => _getInstallId();
237
- const setInstallationId = (id) => { _installId = id; if (_h.canary) _h.canary["x-installation-id"] = id; if (_h.stable) _h.stable["x-installation-id"] = id; };
238
- const generateInstallationId = () => _genInstallId();
239
- const closeSessions = _closeSessions;
240
-
241
- module.exports = { get, refreshHeaders, getHeaders, isRateLimited, getRateLimitRemaining, clearRateLimit, setRateLimit, getInstallationId, setInstallationId, generateInstallationId, closeSessions };
242
- module.exports.default = module.exports;
243
- module.exports.get = get;
244
- module.exports.refreshHeaders = refreshHeaders;
245
- module.exports.getHeaders = getHeaders;
246
- module.exports.isRateLimited = isRateLimited;
247
- module.exports.getRateLimitRemaining = getRateLimitRemaining;
248
- module.exports.clearRateLimit = clearRateLimit;
249
- module.exports.setRateLimit = setRateLimit;
250
- module.exports.getInstallationId = getInstallationId;
251
- module.exports.setInstallationId = setInstallationId;
252
- module.exports.generateInstallationId = generateInstallationId;
253
- module.exports.closeSessions = closeSessions;
99
+ const setCookies = c => { ck = c; if (H.c) H.c["cookie"] = c; if (H.s) H.s["cookie"] = c; };
100
+ const getCookies = () => ck;
101
+ const isRateLimited = isRL, getRateLimitRemaining = getRL, clearRateLimit = clrRL, setRateLimit = setRL;
102
+ module.exports = { get, isRateLimited, getRateLimitRemaining, clearRateLimit, setRateLimit, setCookies, getCookies };
103
+ module.exports.default = module.exports;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rush-mfa",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Discord MFA token generator with auto-updating headers, TLS fallback and IP rate limit handling",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",