djs-selfbot-v13 3.1.6 → 3.1.7

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 (148) hide show
  1. package/README.md +31 -16
  2. package/package.json +15 -8
  3. package/src/client/BaseClient.js +3 -2
  4. package/src/client/Client.js +539 -187
  5. package/src/client/actions/Action.js +13 -18
  6. package/src/client/actions/ActionsManager.js +1 -7
  7. package/src/client/actions/AutoModerationActionExecution.js +0 -1
  8. package/src/client/actions/AutoModerationRuleCreate.js +0 -1
  9. package/src/client/actions/AutoModerationRuleDelete.js +0 -1
  10. package/src/client/actions/AutoModerationRuleUpdate.js +0 -1
  11. package/src/client/actions/InteractionCreate.js +115 -0
  12. package/src/client/actions/MessageCreate.js +4 -0
  13. package/src/client/actions/PresenceUpdate.js +16 -17
  14. package/src/client/websocket/WebSocketManager.js +31 -11
  15. package/src/client/websocket/WebSocketShard.js +38 -39
  16. package/src/client/websocket/handlers/APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE.js +23 -0
  17. package/src/client/websocket/handlers/CALL_CREATE.js +3 -3
  18. package/src/client/websocket/handlers/CALL_DELETE.js +2 -2
  19. package/src/client/websocket/handlers/CALL_UPDATE.js +2 -2
  20. package/src/client/websocket/handlers/CHANNEL_RECIPIENT_ADD.js +13 -16
  21. package/src/client/websocket/handlers/CHANNEL_RECIPIENT_REMOVE.js +11 -11
  22. package/src/client/websocket/handlers/GUILD_APPLICATION_COMMANDS_UPDATE.js +11 -0
  23. package/src/client/websocket/handlers/GUILD_CREATE.js +0 -7
  24. package/src/client/websocket/handlers/GUILD_MEMBER_LIST_UPDATE.js +55 -0
  25. package/src/client/websocket/handlers/GUILD_SOUNDBOARD_SOUNDS_UPDATE.js +0 -0
  26. package/src/client/websocket/handlers/GUILD_SOUNDBOARD_SOUND_CREATE.js +0 -0
  27. package/src/client/websocket/handlers/GUILD_SOUNDBOARD_SOUND_DELETE.js +0 -0
  28. package/src/client/websocket/handlers/GUILD_SOUNDBOARD_SOUND_UPDATE.js +0 -0
  29. package/src/client/websocket/handlers/INTERACTION_CREATE.js +16 -0
  30. package/src/client/websocket/handlers/INTERACTION_FAILURE.js +18 -0
  31. package/src/client/websocket/handlers/INTERACTION_MODAL_CREATE.js +0 -1
  32. package/src/client/websocket/handlers/INTERACTION_SUCCESS.js +30 -0
  33. package/src/client/websocket/handlers/MESSAGE_ACK.js +16 -0
  34. package/src/client/websocket/handlers/READY.js +137 -47
  35. package/src/client/websocket/handlers/RELATIONSHIP_ADD.js +5 -7
  36. package/src/client/websocket/handlers/RELATIONSHIP_REMOVE.js +4 -6
  37. package/src/client/websocket/handlers/RELATIONSHIP_UPDATE.js +9 -32
  38. package/src/client/websocket/handlers/SOUNDBOARD_SOUNDS.js +0 -0
  39. package/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js +8 -2
  40. package/src/client/websocket/handlers/USER_NOTE_UPDATE.js +1 -1
  41. package/src/client/websocket/handlers/USER_SETTINGS_UPDATE.js +5 -1
  42. package/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js +0 -0
  43. package/src/client/websocket/handlers/index.js +20 -15
  44. package/src/errors/Messages.js +69 -24
  45. package/src/index.js +43 -12
  46. package/src/managers/ApplicationCommandManager.js +12 -9
  47. package/src/managers/ApplicationCommandPermissionsManager.js +11 -3
  48. package/src/managers/ChannelManager.js +4 -2
  49. package/src/managers/ClientUserSettingManager.js +279 -161
  50. package/src/managers/DeveloperPortalManager.js +104 -0
  51. package/src/managers/GuildApplicationCommandManager.js +28 -0
  52. package/src/managers/GuildBanManager.js +1 -1
  53. package/src/managers/GuildChannelManager.js +0 -2
  54. package/src/managers/GuildFolderManager.js +24 -0
  55. package/src/managers/GuildForumThreadManager.js +28 -22
  56. package/src/managers/GuildMemberManager.js +216 -40
  57. package/src/managers/GuildSettingManager.js +15 -22
  58. package/src/managers/MessageManager.js +44 -42
  59. package/src/managers/PermissionOverwriteManager.js +1 -1
  60. package/src/managers/ReactionUserManager.js +5 -5
  61. package/src/managers/RelationshipManager.js +74 -81
  62. package/src/managers/SessionManager.js +57 -0
  63. package/src/managers/ThreadManager.js +45 -12
  64. package/src/managers/ThreadMemberManager.js +1 -1
  65. package/src/managers/UserManager.js +10 -6
  66. package/src/rest/APIRequest.js +20 -42
  67. package/src/rest/CaptchaSolver.js +132 -0
  68. package/src/rest/DiscordAPIError.js +16 -17
  69. package/src/rest/RESTManager.js +21 -1
  70. package/src/rest/RequestHandler.js +21 -35
  71. package/src/structures/ApplicationCommand.js +456 -19
  72. package/src/structures/ApplicationRoleConnectionMetadata.js +0 -3
  73. package/src/structures/AutoModerationRule.js +5 -5
  74. package/src/structures/AutocompleteInteraction.js +0 -1
  75. package/src/structures/BaseGuildTextChannel.js +12 -10
  76. package/src/structures/BaseGuildVoiceChannel.js +18 -16
  77. package/src/structures/{CallState.js → Call.js} +12 -17
  78. package/src/structures/CategoryChannel.js +0 -2
  79. package/src/structures/Channel.js +3 -2
  80. package/src/structures/ClientApplication.js +204 -0
  81. package/src/structures/ClientPresence.js +8 -12
  82. package/src/structures/ClientUser.js +336 -117
  83. package/src/structures/ContextMenuInteraction.js +1 -1
  84. package/src/structures/DMChannel.js +92 -29
  85. package/src/structures/DeveloperPortalApplication.js +520 -0
  86. package/src/structures/ForumChannel.js +10 -0
  87. package/src/structures/Guild.js +271 -135
  88. package/src/structures/GuildAuditLogs.js +5 -0
  89. package/src/structures/GuildChannel.js +2 -16
  90. package/src/structures/GuildFolder.js +75 -0
  91. package/src/structures/GuildMember.js +145 -27
  92. package/src/structures/Interaction.js +62 -1
  93. package/src/structures/InteractionResponse.js +114 -0
  94. package/src/structures/Invite.js +52 -35
  95. package/src/structures/Message.js +202 -222
  96. package/src/structures/MessageAttachment.js +0 -11
  97. package/src/structures/MessageButton.js +67 -1
  98. package/src/structures/MessageEmbed.js +1 -1
  99. package/src/structures/MessageMentions.js +2 -3
  100. package/src/structures/MessagePayload.js +46 -4
  101. package/src/structures/MessageReaction.js +1 -1
  102. package/src/structures/MessageSelectMenu.js +252 -1
  103. package/src/structures/Modal.js +180 -75
  104. package/src/structures/PartialGroupDMChannel.js +433 -0
  105. package/src/structures/Presence.js +2 -2
  106. package/src/structures/RichPresence.js +34 -14
  107. package/src/structures/Role.js +2 -18
  108. package/src/structures/SelectMenuInteraction.js +151 -2
  109. package/src/structures/Session.js +81 -0
  110. package/src/structures/Team.js +49 -0
  111. package/src/structures/TextInputComponent.js +70 -0
  112. package/src/structures/ThreadChannel.js +19 -0
  113. package/src/structures/User.js +345 -117
  114. package/src/structures/UserContextMenuInteraction.js +2 -2
  115. package/src/structures/VoiceState.js +39 -74
  116. package/src/structures/WebEmbed.js +52 -38
  117. package/src/structures/Webhook.js +11 -17
  118. package/src/structures/interfaces/Application.js +23 -146
  119. package/src/structures/interfaces/TextBasedChannel.js +256 -411
  120. package/src/util/ApplicationFlags.js +1 -1
  121. package/src/util/Constants.js +284 -106
  122. package/src/util/Formatters.js +2 -16
  123. package/src/util/LimitedCollection.js +1 -1
  124. package/src/util/Options.js +68 -48
  125. package/src/util/Permissions.js +0 -5
  126. package/src/util/PurchasedFlags.js +0 -2
  127. package/src/util/RemoteAuth.js +356 -221
  128. package/src/util/Sweepers.js +1 -1
  129. package/src/util/Util.js +36 -76
  130. package/src/util/Voice.js +1456 -0
  131. package/src/util/arRPC/index.js +229 -0
  132. package/src/util/arRPC/process/detectable.json +1 -0
  133. package/src/util/arRPC/process/index.js +102 -0
  134. package/src/util/arRPC/process/native/index.js +5 -0
  135. package/src/util/arRPC/process/native/linux.js +37 -0
  136. package/src/util/arRPC/process/native/win32.js +25 -0
  137. package/src/util/arRPC/transports/ipc.js +281 -0
  138. package/src/util/arRPC/transports/websocket.js +128 -0
  139. package/typings/enums.d.ts +73 -18
  140. package/typings/index.d.ts +1249 -897
  141. package/typings/rawDataTypes.d.ts +9 -68
  142. package/src/client/websocket/handlers/USER_REQUIRED_ACTION_UPDATE.js +0 -78
  143. package/src/client/websocket/handlers/VOICE_CHANNEL_STATUS_UPDATE.js +0 -12
  144. package/src/managers/UserNoteManager.js +0 -53
  145. package/src/structures/GroupDMChannel.js +0 -387
  146. package/src/util/AttachmentFlags.js +0 -38
  147. package/src/util/InviteFlags.js +0 -29
  148. package/src/util/RoleFlags.js +0 -37
@@ -1,13 +1,14 @@
1
1
  'use strict';
2
-
3
- const { Buffer } = require('node:buffer');
4
- const crypto = require('node:crypto');
2
+ const { Buffer } = require('buffer');
3
+ const crypto = require('crypto');
5
4
  const EventEmitter = require('node:events');
6
- const { StringDecoder } = require('node:string_decoder');
7
5
  const { setTimeout } = require('node:timers');
6
+ const { StringDecoder } = require('string_decoder');
7
+ const chalk = require('chalk');
8
8
  const fetch = require('node-fetch');
9
+ const { encode: urlsafe_b64encode } = require('safe-base64');
9
10
  const WebSocket = require('ws');
10
- const { UserAgent } = require('./Constants');
11
+ const { defaultUA } = require('./Constants');
11
12
  const Options = require('./Options');
12
13
 
13
14
  const defaultClientOptions = Options.createDefault();
@@ -21,9 +22,9 @@ const receiveEvent = {
21
22
  NONCE_PROOF: 'nonce_proof',
22
23
  PENDING_REMOTE_INIT: 'pending_remote_init',
23
24
  HEARTBEAT_ACK: 'heartbeat_ack',
24
- PENDING_TICKET: 'pending_ticket',
25
+ PENDING_LOGIN: 'pending_ticket',
25
26
  CANCEL: 'cancel',
26
- PENDING_LOGIN: 'pending_login',
27
+ SUCCESS: 'pending_login',
27
28
  };
28
29
 
29
30
  const sendEvent = {
@@ -36,167 +37,266 @@ const Event = {
36
37
  READY: 'ready',
37
38
  ERROR: 'error',
38
39
  CANCEL: 'cancel',
39
- WAIT_SCAN: 'pending',
40
+ WAIT: 'pending',
41
+ SUCCESS: 'success',
40
42
  FINISH: 'finish',
41
43
  CLOSED: 'closed',
42
- DEBUG: 'debug',
43
44
  };
44
45
 
45
46
  /**
46
- * Discord Auth QR
47
+ * @typedef {Object} DiscordAuthWebsocketOptions
48
+ * @property {?boolean} [debug=false] Log debug info
49
+ * @property {?boolean} [hiddenLog=false] Hide log ?
50
+ * @property {?boolean} [autoLogin=false] Automatically login (DiscordJS.Client Login) ?
51
+ * @property {?boolean} [failIfError=true] Throw error ?
52
+ * @property {?boolean} [generateQR=true] Create QR Code ?
53
+ * @property {?number} [apiVersion=9] API Version
54
+ * @property {?string} [userAgent] User Agent
55
+ * @property {?Object.<string,string>} [wsProperties] Web Socket Properties
56
+ */
57
+
58
+ /**
59
+ * Discord Auth QR (Discord.RemoteAuth will be removed in the future, v13.9.0 release)
47
60
  * @extends {EventEmitter}
48
61
  * @abstract
49
62
  */
50
63
  class DiscordAuthWebsocket extends EventEmitter {
51
- #ws = null;
52
- #heartbeatInterval = null;
53
- #expire = null;
54
- #publicKey = null;
55
- #privateKey = null;
56
- #ticket = null;
57
- #fingerprint = '';
58
- #userDecryptString = '';
59
-
60
64
  /**
61
65
  * Creates a new DiscordAuthWebsocket instance.
66
+ * @param {?DiscordAuthWebsocketOptions} options Options
62
67
  */
63
- constructor() {
68
+ constructor(options) {
64
69
  super();
65
- this.token = '';
66
- }
70
+ /**
71
+ * WebSocket
72
+ * @type {?WebSocket}
73
+ */
74
+ this.ws = null;
75
+ /**
76
+ * Heartbeat Interval
77
+ * @type {?number}
78
+ */
79
+ this.heartbeatInterval = NaN;
80
+ this._expire = NaN;
81
+ this.key = null;
82
+ /**
83
+ * User (Scan QR Code)
84
+ * @type {?Object}
85
+ */
86
+ this.user = null;
87
+ /**
88
+ * Temporary Token (Scan QR Code)
89
+ * @type {?string}
90
+ */
91
+ this.token = undefined;
92
+ /**
93
+ * Real Token (Login)
94
+ * @type {?string}
95
+ */
96
+ this.realToken = undefined;
97
+ /**
98
+ * Fingerprint (QR Code)
99
+ * @type {?string}
100
+ */
101
+ this.fingerprint = null;
67
102
 
68
- /**
69
- * @type {string}
70
- */
71
- get AuthURL() {
72
- return baseURL + this.#fingerprint;
103
+ /**
104
+ * Captcha Handler
105
+ * @type {Function}
106
+ * @param {Captcha} data hcaptcha data
107
+ * @returns {Promise<string>} Captcha token
108
+ */
109
+ // eslint-disable-next-line no-unused-vars
110
+ this.captchaSolver = data =>
111
+ new Promise((resolve, reject) => {
112
+ reject(
113
+ new Error(`
114
+ Captcha Handler not found - Please set captchaSolver option
115
+ Example captchaSolver function:
116
+
117
+ new DiscordAuthWebsocket({
118
+ captchaSolver: async (data) => {
119
+ const token = await hcaptchaSolver(data.captcha_sitekey, 'discord.com');
120
+ return token;
73
121
  }
122
+ });
74
123
 
75
- /**
76
- * @type {Date}
77
- */
78
- get exprire() {
79
- return this.#expire;
80
- }
124
+ `),
125
+ );
126
+ });
81
127
 
128
+ /**
129
+ * Captcha Cache
130
+ * @type {?Captcha}
131
+ */
132
+ this.captchaCache = null;
133
+
134
+ this._validateOptions(options);
135
+
136
+ this.callFindRealTokenCount = 0;
137
+ }
82
138
  /**
83
- * @type {UserRaw}
139
+ * Get expire time
140
+ * @type {string} Expire time
141
+ * @readonly
84
142
  */
85
- get user() {
86
- return DiscordAuthWebsocket.decryptUser(this.#userDecryptString);
143
+ get exprireTime() {
144
+ return this._expire.toLocaleString('en-US');
87
145
  }
88
-
89
- #createWebSocket(url) {
90
- this.#ws = new WebSocket(url, {
146
+ _validateOptions(options = {}) {
147
+ /**
148
+ * Options
149
+ * @type {?DiscordAuthWebsocketOptions}
150
+ */
151
+ this.options = {
152
+ debug: false,
153
+ hiddenLog: false,
154
+ autoLogin: false,
155
+ failIfError: true,
156
+ generateQR: true,
157
+ apiVersion: 9,
158
+ userAgent: defaultUA,
159
+ wsProperties: defaultClientOptions.ws.properties,
160
+ captchaSolver: () => new Error('Captcha Handler not found. Please set captchaSolver option.'),
161
+ };
162
+ if (typeof options == 'object') {
163
+ if (typeof options.debug == 'boolean') this.options.debug = options.debug;
164
+ if (typeof options.hiddenLog == 'boolean') this.options.hiddenLog = options.hiddenLog;
165
+ if (typeof options.autoLogin == 'boolean') this.options.autoLogin = options.autoLogin;
166
+ if (typeof options.failIfError == 'boolean') this.options.failIfError = options.failIfError;
167
+ if (typeof options.generateQR == 'boolean') this.options.generateQR = options.generateQR;
168
+ if (typeof options.apiVersion == 'number') this.options.apiVersion = options.apiVersion;
169
+ if (typeof options.userAgent == 'string') this.options.userAgent = options.userAgent;
170
+ if (typeof options.wsProperties == 'object') this.options.wsProperties = options.wsProperties;
171
+ if (typeof options.captchaSolver == 'function') this.captchaSolver = options.captchaSolver;
172
+ }
173
+ }
174
+ _createWebSocket(url) {
175
+ this.ws = new WebSocket(url, {
91
176
  headers: {
92
177
  Origin: 'https://discord.com',
93
- 'User-Agent': UserAgent,
178
+ 'User-Agent': this.options.userAgent,
94
179
  },
95
180
  });
96
- this.#handleWebSocket();
181
+ this._handleWebSocket();
97
182
  }
98
-
99
- #handleWebSocket() {
100
- this.#ws.on('error', error => {
101
- /**
102
- * WS Error
103
- * @event DiscordAuthWebsocket#error
104
- * @param {Error} error Error
105
- */
106
- this.emit(Event.ERROR, error);
183
+ _handleWebSocket() {
184
+ this.ws.on('error', error => {
185
+ this._logger('error', error);
186
+ });
187
+ this.ws.on('open', () => {
188
+ this._logger('debug', 'Client Connected');
107
189
  });
108
- this.#ws.on('open', () => {
109
- /**
110
- * Debug Event
111
- * @event DiscordAuthWebsocket#debug
112
- * @param {string} msg Debug msg
113
- */
114
- this.emit(Event.DEBUG, '[WS] Client Connected');
190
+ this.ws.on('close', () => {
191
+ this._logger('debug', 'Connection closed.');
115
192
  });
116
- this.#ws.on('close', () => {
117
- this.emit(Event.DEBUG, '[WS] Connection closed');
193
+ this.ws.on('message', message => {
194
+ this._handleMessage(JSON.parse(message));
118
195
  });
119
- this.#ws.on('message', this.#handleMessage.bind(this));
120
196
  }
121
-
122
- #handleMessage(message) {
123
- message = JSON.parse(message);
197
+ _handleMessage(message) {
124
198
  switch (message.op) {
125
199
  case receiveEvent.HELLO: {
126
- this.#ready(message);
200
+ this._ready(message);
127
201
  break;
128
202
  }
129
-
130
203
  case receiveEvent.NONCE_PROOF: {
131
- this.#receiveNonceProof(message);
204
+ this._receiveNonceProof(message);
132
205
  break;
133
206
  }
134
-
135
207
  case receiveEvent.PENDING_REMOTE_INIT: {
136
- this.#fingerprint = message.fingerprint;
137
- /**
138
- * Ready Event
139
- * @event DiscordAuthWebsocket#ready
140
- * @param {DiscordAuthWebsocket} client WS
141
- */
142
- this.emit(Event.READY, this);
208
+ this._pendingRemoteInit(message);
143
209
  break;
144
210
  }
145
-
146
211
  case receiveEvent.HEARTBEAT_ACK: {
147
- this.emit(Event.DEBUG, `Heartbeat acknowledged.`);
148
- this.#heartbeatAck();
212
+ this._logger('debug', 'Heartbeat acknowledged.');
213
+ this._heartbeatAck();
149
214
  break;
150
215
  }
151
-
152
- case receiveEvent.PENDING_TICKET: {
153
- this.#pendingLogin(message);
216
+ case receiveEvent.PENDING_LOGIN: {
217
+ this._pendingLogin(message);
154
218
  break;
155
219
  }
156
-
157
220
  case receiveEvent.CANCEL: {
221
+ this._logger('debug', 'Cancel login.');
158
222
  /**
159
- * Cancel
223
+ * Emitted whenever a user cancels the login process.
160
224
  * @event DiscordAuthWebsocket#cancel
161
- * @param {DiscordAuthWebsocket} client WS
225
+ * @param {object} user User (Raw)
162
226
  */
163
- this.emit(Event.CANCEL, this);
227
+ this.emit(Event.CANCEL, this.user);
164
228
  this.destroy();
165
229
  break;
166
230
  }
167
-
168
- case receiveEvent.PENDING_LOGIN: {
169
- this.#ticket = message.ticket;
170
- this.#findRealToken();
231
+ case receiveEvent.SUCCESS: {
232
+ this._logger('debug', 'Receive Token - Login Success.', message.ticket);
233
+ /**
234
+ * Emitted whenever a token is created. (Fake token)
235
+ * @event DiscordAuthWebsocket#success
236
+ * @param {object} user Discord User
237
+ * @param {string} token Discord Token (Fake)
238
+ */
239
+ this.emit(Event.SUCCESS, this.user, message.ticket);
240
+ this.token = message.ticket;
241
+ this._findRealToken();
242
+ this._logger('default', 'Get token success.');
171
243
  break;
172
244
  }
245
+ default: {
246
+ this._logger('debug', `Unknown op: ${message.op}`, message);
247
+ }
173
248
  }
174
249
  }
175
-
176
- #send(op, data) {
177
- if (!this.#ws) return;
250
+ _logger(type = 'default', ...message) {
251
+ if (this.options.hiddenLog) return;
252
+ switch (type.toLowerCase()) {
253
+ case 'error': {
254
+ // eslint-disable-next-line no-unused-expressions
255
+ this.options.failIfError
256
+ ? this._throwError(new Error(message[0]))
257
+ : console.error(chalk.red(`[DiscordRemoteAuth] ERROR`), ...message);
258
+ break;
259
+ }
260
+ case 'default': {
261
+ console.log(chalk.green(`[DiscordRemoteAuth]`), ...message);
262
+ break;
263
+ }
264
+ case 'debug': {
265
+ if (this.options.debug) console.log(chalk.yellow(`[DiscordRemoteAuth] DEBUG`), ...message);
266
+ break;
267
+ }
268
+ }
269
+ }
270
+ _throwError(error) {
271
+ console.log(chalk.red(`[DiscordRemoteAuth] ERROR`), error);
272
+ throw error;
273
+ }
274
+ _send(op, data) {
275
+ if (!this.ws) this._throwError(new Error('WebSocket is not connected.'));
178
276
  let payload = { op: op };
179
277
  if (data !== null) payload = { ...payload, ...data };
180
- this.#ws.send(JSON.stringify(payload));
278
+ this._logger('debug', `Send Data:`, payload);
279
+ this.ws.send(JSON.stringify(payload));
181
280
  }
182
-
183
- #heartbeatAck() {
281
+ _heartbeat() {
282
+ this._send(sendEvent.HEARTBEAT);
283
+ }
284
+ _heartbeatAck() {
184
285
  setTimeout(() => {
185
- this.#send(sendEvent.HEARTBEAT);
186
- }, this.#heartbeatInterval).unref();
286
+ this._heartbeat();
287
+ }, this.heartbeatInterval).unref();
187
288
  }
188
-
189
- #ready(data) {
190
- this.emit(Event.DEBUG, 'Attempting server handshake...');
191
- this.#expire = new Date(Date.now() + data.timeout_ms);
192
- this.#heartbeatInterval = data.heartbeat_interval;
193
- this.#createKey();
194
- this.#heartbeatAck();
195
- this.#init();
289
+ _ready(data) {
290
+ this._logger('debug', 'Attempting server handshake...');
291
+ this._expire = new Date(Date.now() + data.timeout_ms);
292
+ this.heartbeatInterval = data.heartbeat_interval;
293
+ this._createKey();
294
+ this._heartbeatAck();
295
+ this._init();
196
296
  }
197
-
198
- #createKey() {
199
- const key = crypto.generateKeyPairSync('rsa', {
297
+ _createKey() {
298
+ if (this.key) this._throwError(new Error('Key is already created.'));
299
+ this.key = crypto.generateKeyPairSync('rsa', {
200
300
  modulusLength: 2048,
201
301
  publicKeyEncoding: {
202
302
  type: 'spki',
@@ -207,41 +307,35 @@ class DiscordAuthWebsocket extends EventEmitter {
207
307
  format: 'pem',
208
308
  },
209
309
  });
210
- this.#privateKey = key.privateKey;
211
- this.#publicKey = key.publicKey;
212
310
  }
213
-
214
- #encodePublicKey() {
311
+ _createPublicKey() {
312
+ if (!this.key) this._throwError(new Error('Key is not created.'));
313
+ this._logger('debug', 'Generating public key...');
215
314
  const decoder = new StringDecoder('utf-8');
216
- let pub_key = decoder.write(this.#publicKey);
315
+ let pub_key = decoder.write(this.key.publicKey);
217
316
  pub_key = pub_key.split('\n').slice(1, -2).join('');
317
+ this._logger('debug', 'Public key generated.', pub_key);
218
318
  return pub_key;
219
319
  }
220
-
221
- #init() {
222
- const public_key = this.#encodePublicKey();
223
- this.#send(sendEvent.INIT, { encoded_public_key: public_key });
320
+ _init() {
321
+ const public_key = this._createPublicKey();
322
+ this._send(sendEvent.INIT, { encoded_public_key: public_key });
224
323
  }
225
-
226
- #receiveNonceProof(data) {
324
+ _receiveNonceProof(data) {
227
325
  const nonce = data.encrypted_nonce;
228
- const decrypted_nonce = this.#decryptPayload(nonce);
229
- const proof = crypto
230
- .createHash('sha256')
231
- .update(decrypted_nonce)
232
- .digest()
233
- .toString('base64')
234
- .replace(/\+/g, '-')
235
- .replace(/\//g, '_')
236
- .replace(/=+/, '')
237
- .replace(/\s+$/, '');
238
- this.#send(sendEvent.NONCE_PROOF, { proof: proof });
326
+ const decrypted_nonce = this._decryptPayload(nonce);
327
+ let proof = crypto.createHash('sha256').update(decrypted_nonce).digest();
328
+ proof = urlsafe_b64encode(proof);
329
+ proof = proof.replace(/\s+$/, '');
330
+ this._send(sendEvent.NONCE_PROOF, { proof: proof });
331
+ this._logger('debug', `Nonce proof decrypted:`, proof);
239
332
  }
240
-
241
- #decryptPayload(encrypted_payload) {
333
+ _decryptPayload(encrypted_payload) {
334
+ if (!this.key) this._throwError(new Error('Key is not created.'));
242
335
  const payload = Buffer.from(encrypted_payload, 'base64');
336
+ this._logger('debug', `Encrypted Payload (Buffer):`, payload);
243
337
  const decoder = new StringDecoder('utf-8');
244
- const private_key = decoder.write(this.#privateKey);
338
+ const private_key = decoder.write(this.key.privateKey);
245
339
  const data = crypto.privateDecrypt(
246
340
  {
247
341
  key: private_key,
@@ -250,129 +344,170 @@ class DiscordAuthWebsocket extends EventEmitter {
250
344
  },
251
345
  payload,
252
346
  );
347
+ this._logger('debug', `Decrypted Payload:`, data.toString());
253
348
  return data;
254
349
  }
255
-
256
- #pendingLogin(data) {
257
- const user_data = this.#decryptPayload(data.encrypted_user_payload);
258
- this.#userDecryptString = user_data.toString();
259
-
260
- /**
261
- * @typedef {Object} UserRaw
262
- * @property {Snowflake} id
263
- * @property {string} username
264
- * @property {number} discriminator
265
- * @property {string} avatar
266
- */
267
-
350
+ _pendingLogin(data) {
351
+ const user_data = this._decryptPayload(data.encrypted_user_payload);
352
+ const user = new User(user_data.toString());
353
+ this.user = user;
268
354
  /**
269
355
  * Emitted whenever a user is scan QR Code.
270
356
  * @event DiscordAuthWebsocket#pending
271
- * @param {UserRaw} user Discord User Raw
357
+ * @param {object} user Discord User Raw
272
358
  */
273
- this.emit(Event.WAIT_SCAN, this.user);
359
+ this.emit(Event.WAIT, user);
360
+ this._logger('debug', 'Waiting for user to finish login...');
361
+ this.user.prettyPrint(this);
362
+ this._logger('default', 'Please check your phone again to confirm login.');
274
363
  }
275
-
276
- #awaitLogin(client) {
277
- return new Promise(r => {
278
- this.once(Event.FINISH, token => {
279
- r(client.login(token));
280
- });
364
+ _pendingRemoteInit(data) {
365
+ this._logger('debug', `Pending Remote Init:`, data);
366
+ /**
367
+ * Emitted whenever a url is created.
368
+ * @event DiscordAuthWebsocket#ready
369
+ * @param {string} fingerprint Fingerprint
370
+ * @param {string} url DiscordAuthWebsocket
371
+ */
372
+ this.emit(Event.READY, data.fingerprint, `${baseURL}${data.fingerprint}`);
373
+ this.fingerprint = data.fingerprint;
374
+ if (this.options.generateQR) this.generateQR();
375
+ }
376
+ _awaitLogin(client) {
377
+ this.once(Event.FINISH, (user, token) => {
378
+ this._logger('debug', 'Create login state...', user, token);
379
+ client.login(token);
281
380
  });
282
381
  }
283
-
284
382
  /**
285
- * Connect WS
286
- * @param {Client} [client] DiscordJS Client
287
- * @returns {Promise<void>}
383
+ * Connect to DiscordAuthWebsocket.
384
+ * @param {?Client} client Using only for auto login.
385
+ * @returns {undefined}
288
386
  */
289
387
  connect(client) {
290
- this.#createWebSocket(wsURL);
291
- if (client) {
292
- return this.#awaitLogin(client);
293
- } else {
294
- return Promise.resolve();
295
- }
388
+ this._createWebSocket(wsURL);
389
+ if (client && this.options.autoLogin) this._awaitLogin(client);
296
390
  }
297
-
298
391
  /**
299
- * Destroy client
300
- * @returns {void}
392
+ * Disconnect from DiscordAuthWebsocket.
393
+ * @returns {undefined}
301
394
  */
302
395
  destroy() {
303
- if (!this.ws) return;
396
+ if (!this.ws) this._throwError(new Error('WebSocket is not connected.'));
304
397
  this.ws.close();
305
- this.emit(Event.DEBUG, 'WebSocket closed.');
306
398
  /**
307
399
  * Emitted whenever a connection is closed.
308
400
  * @event DiscordAuthWebsocket#closed
401
+ * @param {boolean} loginState Login state
309
402
  */
310
- this.emit(Event.CLOSED);
403
+ this.emit(Event.CLOSED, this.token);
404
+ this._logger('debug', 'WebSocket closed.');
311
405
  }
312
-
313
406
  /**
314
407
  * Generate QR code for user to scan (Terminal)
315
- * @returns {void}
408
+ * @returns {undefined}
316
409
  */
317
410
  generateQR() {
318
- if (!this.#fingerprint) return;
319
- require('@aikochan2k6/qrcode-terminal').generate(this.AuthURL, {
411
+ if (!this.fingerprint) this._throwError(new Error('Fingerprint is not created.'));
412
+ require('@aikochan2k6/qrcode-terminal').generate(`${baseURL}${this.fingerprint}`, {
320
413
  small: true,
321
414
  });
415
+ this._logger('default', `Please scan the QR code to continue.\nQR Code will expire in ${this.exprireTime}`);
322
416
  }
323
417
 
324
- #findRealToken() {
325
- return fetch(`https://discord.com/api/v9/users/@me/remote-auth/login`, {
326
- method: 'POST',
327
- headers: {
328
- Accept: '*/*',
329
- 'Accept-Language': 'en-US',
330
- 'Content-Type': 'application/json',
331
- 'Sec-Fetch-Dest': 'empty',
332
- 'Sec-Fetch-Mode': 'cors',
333
- 'Sec-Fetch-Site': 'same-origin',
334
- 'X-Debug-Options': 'bugReporterEnabled',
335
- 'X-Super-Properties': `${Buffer.from(JSON.stringify(defaultClientOptions.ws.properties), 'ascii').toString(
336
- 'base64',
337
- )}`,
338
- 'X-Discord-Locale': 'en-US',
339
- 'User-Agent': UserAgent,
340
- Referer: 'https://discord.com/channels/@me',
341
- Connection: 'keep-alive',
342
- Origin: 'https://discord.com',
343
- },
344
- body: JSON.stringify({
345
- ticket: this.#ticket,
346
- }),
347
- })
348
- .then(r => r.json())
349
- .then(res => {
350
- if (res.encrypted_token) {
351
- this.token = this.#decryptPayload(res.encrypted_token).toString();
352
- }
353
- /**
354
- * Emitted whenever a real token is found.
355
- * @event DiscordAuthWebsocket#finish
356
- * @param {string} token Discord Token
357
- */
358
- this.emit(Event.FINISH, this.token);
359
- this.destroy();
418
+ async _findRealToken(captchaSolveData) {
419
+ this.callFindRealTokenCount++;
420
+ if (!this.token) return this._throwError(new Error('Token is not created.'));
421
+ if (!captchaSolveData && this.captchaCache) return this._throwError(new Error('Captcha is not solved.'));
422
+ if (this.callFindRealTokenCount > 5) {
423
+ return this._throwError(
424
+ new Error(
425
+ `Failed to find real token (${this.callFindRealTokenCount} times) ${this.captchaCache ? '[Captcha]' : ''}`,
426
+ ),
427
+ );
428
+ }
429
+ this._logger('debug', 'Find real token...');
430
+ const res = await (
431
+ await fetch(`https://discord.com/api/v${this.options.apiVersion}/users/@me/remote-auth/login`, {
432
+ method: 'POST',
433
+ headers: {
434
+ Accept: '*/*',
435
+ 'Accept-Language': 'en-US',
436
+ 'Content-Type': 'application/json',
437
+ 'Sec-Fetch-Dest': 'empty',
438
+ 'Sec-Fetch-Mode': 'cors',
439
+ 'Sec-Fetch-Site': 'same-origin',
440
+ 'X-Debug-Options': 'bugReporterEnabled',
441
+ 'X-Super-Properties': `${Buffer.from(JSON.stringify(this.options.wsProperties), 'ascii').toString('base64')}`,
442
+ 'X-Discord-Locale': 'en-US',
443
+ 'User-Agent': this.options.userAgent,
444
+ Referer: 'https://discord.com/channels/@me',
445
+ Connection: 'keep-alive',
446
+ Origin: 'https://discord.com',
447
+ },
448
+ body: JSON.stringify(
449
+ captchaSolveData
450
+ ? {
451
+ ticket: this.token,
452
+ captcha_rqtoken: this.captchaCache.captcha_rqtoken,
453
+ captcha_key: captchaSolveData,
454
+ }
455
+ : {
456
+ ticket: this.token,
457
+ },
458
+ ),
360
459
  })
361
- .catch(() => false);
460
+ ).json();
461
+ if (res?.captcha_key) {
462
+ this.captchaCache = res;
463
+ } else if (!res.encrypted_token) {
464
+ this._throwError(new Error('Request failed. Please try again.', res));
465
+ this.captchaCache = null;
466
+ }
467
+ if (!res && this.captchaCache) {
468
+ this._logger('default', 'Captcha is detected. Please solve the captcha to continue.');
469
+ this._logger('debug', 'Try call captchaSolver()', this.captchaCache);
470
+ const token = await this.options.captchaSolver(this.captchaCache);
471
+ return this._findRealToken(token);
472
+ }
473
+ this.realToken = this._decryptPayload(res.encrypted_token).toString();
474
+ /**
475
+ * Emitted whenever a real token is found.
476
+ * @event DiscordAuthWebsocket#finish
477
+ * @param {object} user User
478
+ * @param {string} token Real token
479
+ */
480
+ this.emit(Event.FINISH, this.user, this.realToken);
481
+ return this;
362
482
  }
483
+ }
363
484
 
364
- static decryptUser(payload) {
485
+ class User {
486
+ constructor(payload) {
365
487
  const values = payload.split(':');
366
- const id = values[0];
367
- const username = values[3];
368
- const discriminator = values[1];
369
- const avatar = values[2];
370
- return {
371
- id,
372
- username,
373
- discriminator,
374
- avatar,
375
- };
488
+ this.id = values[0];
489
+ this.username = values[3];
490
+ this.discriminator = values[1];
491
+ this.avatar = values[2];
492
+ return this;
493
+ }
494
+ get avatarURL() {
495
+ return `https://cdn.discordapp.com/avatars/${this.id}/${this.avatar}.${
496
+ this.avatar.startsWith('a_') ? 'gif' : 'png'
497
+ }`;
498
+ }
499
+ get tag() {
500
+ return `${this.username}#${this.discriminator}`;
501
+ }
502
+ prettyPrint(RemoteAuth) {
503
+ let string = `\n`;
504
+ string += ` ${chalk.bgBlue('User:')} `;
505
+ string += `${this.tag} (${this.id})\n`;
506
+ string += ` ${chalk.bgGreen('Avatar URL:')} `;
507
+ string += chalk.cyan(`${this.avatarURL}\n`);
508
+ string += ` ${chalk.bgMagenta('Token:')} `;
509
+ string += chalk.red(`${this.token ? this.token : 'Unknown'}`);
510
+ RemoteAuth._logger('default', string);
376
511
  }
377
512
  }
378
513