-
Notifications
You must be signed in to change notification settings - Fork 16
/
index.js
251 lines (220 loc) · 12.3 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
// @ts-check
/// <reference path="./types.d.ts" />
// From https://github.com/rigwild/discord-self-bot-console
{
var gid = '' // Current guild id
var cid = '' // Current channel id
var authHeader = '' // Authorization token
var autoUpdateToken = undefined // Should the token be updated automatically when a request with the token is intercepted?
// Call this to update `cid` and `gid` to current channel and guild id
var update_guildId_and_channelId_withCurrentlyVisible = (log = true) => {
gid = window.location.href.split('/').slice(4)[0]
cid = window.location.href.split('/').slice(4)[1]
if (log) {
console.log(`\`gid\` was set to the guild id you are currently looking at (${gid})`)
console.log(`\`cid\` was set to the channel id you are currently looking at (${cid})`)
}
}
var id = update_guildId_and_channelId_withCurrentlyVisible
/** @type {import('./types').api['delay']} */
var delay = ms => new Promise(res => setTimeout(res, ms))
// prettier-ignore
var qs = obj => Object.entries(obj).map(([k, v]) => `${k}=${v}`).join('&')
/** @type {import('./types').api['apiCall']} */
var apiCall = (apiPath, body, method = 'GET', options = {}) => {
if (!authHeader) throw new Error("The authorization token is missing. Did you forget to set it? `authHeader = 'your_token'`")
const fetchOptions = {
body: body ? body : undefined,
method,
headers: {
Accept: '*/*',
'Accept-Language': 'en-US',
Authorization: authHeader,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) discord/1.0.9015 Chrome/108.0.5359.215 Electron/22.3.12 Safari/537.36',
'X-Super-Properties': btoa(
JSON.stringify({
os: 'Windows',
browser: 'Discord Client',
release_channel: 'stable',
client_version: '1.0.9163',
os_version: '10.0.22631',
os_arch: 'x64',
app_arch: 'x64',
system_locale: 'en-US',
browser_user_agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) discord/1.0.9163 Chrome/124.0.6367.243 Electron/30.2.0 Safari/537.36',
browser_version: '30.2.0',
os_sdk_version: '22631',
client_build_number: 327338,
native_build_number: 52153,
client_event_source: null,
}),
),
},
...options,
}
const isFormData = body?.constructor?.name === 'FormData'
if (!isFormData) {
fetchOptions.headers['Content-Type'] = 'application/json'
fetchOptions.body = JSON.stringify(body)
}
return fetch(`https://discord.com/api/v9${apiPath}`, fetchOptions)
.then(res => {
if (res.ok) return res.json()
throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`)
})
.catch(err => {
console.error(err)
throw new Error('An error occurred while fetching the API.')
})
}
/** @type {import('./types').api} */
var api = {
getMessages: (channelOrThreadId, limit = 100, params = {}) => apiCall(`/channels/${channelOrThreadId}/messages?limit=${limit ?? 100}&${qs(params)}`),
sendMessage: (channelOrThreadId, message, tts, body = {}) => apiCall(`/channels/${channelOrThreadId}/messages`, { content: message, tts: !!tts, ...body }, 'POST'),
replyToMessage: (channelOrThreadId, repliedMessageId, message, tts, body = {}) =>
apiCall(`/channels/${channelOrThreadId}/messages`, { content: message, message_reference: { message_id: repliedMessageId }, tts: !!tts, ...body }, 'POST'),
editMessage: (channelOrThreadId, messageId, newMessage, body = {}) => apiCall(`/channels/${channelOrThreadId}/messages/${messageId}`, { content: newMessage, ...body }, 'PATCH'),
deleteMessage: (channelOrThreadId, messageId) => apiCall(`/channels/${channelOrThreadId}/messages/${messageId}`, null, 'DELETE'),
createThread: (channelId, toOpenThreadInmessageId, name, autoArchiveDuration = 1440, body = {}) =>
apiCall(`/channels/${channelId}/messages/${toOpenThreadInmessageId}/threads`, { name, auto_archive_duration: autoArchiveDuration, location: 'Message', type: 11, ...body }, 'POST'),
createThreadWithoutMessage: (channelId, name, autoArchiveDuration = 1440, body = {}) =>
apiCall(`/channels/${channelId}/threads`, { name, auto_archive_duration: autoArchiveDuration, location: 'Message', type: 11, ...body }, 'POST'),
deleteThread: threadId => apiCall(`/channels/${threadId}`, null, 'DELETE'),
// Use this generator: https://discord.club/dashboard
// Click `+` at the bottom in the embed section then copy the `embed` key in the JSON output.
// Does not work with user account anymore!
sendEmbed: (channelOrThreadId, embed = { title: 'Title', description: 'Description' }) => apiCall(`/channels/${channelOrThreadId}/messages`, { embed }, 'POST'),
getRoles: guildId => apiCall(`/guilds/${guildId}/roles`),
createRole: (guildId, name) => apiCall(`/guilds/${guildId}/roles`, { name }, 'POST'),
deleteRole: (guildId, roleId) => apiCall(`/guilds/${guildId}/roles/${roleId}`, null, 'DELETE'),
getBans: guildId => apiCall(`/guilds/${guildId}/bans`),
banUser: (guildId, userId, reason) => apiCall(`/guilds/${guildId}/bans/${userId}`, { delete_message_days: '7', reason }, 'PUT'),
unbanUser: (guildId, userId) => apiCall(`/guilds/${guildId}/bans/${userId}`, null, 'DELETE'),
kickUser: (guildId, userId) => apiCall(`/guilds/${guildId}/members/${userId}`, null, 'DELETE'),
addRole: (guildId, userId, roleId) => apiCall(`/guilds/${guildId}/members/${userId}/roles/${roleId}`, null, 'PUT'),
removeRole: (guildId, userId, roleId) => apiCall(`/guilds/${guildId}/members/${userId}/roles/${roleId}`, null, 'DELETE'),
auditLogs: guildId => apiCall(`/guilds/${guildId}/audit-logs`),
getChannels: guildId => apiCall(`/guilds/${guildId}/channels`),
createChannel: (guildId, name, type) => apiCall(`/guilds/${guildId}/channels`, { name, type }, 'POST'),
deleteChannel: channelId => apiCall(`/channels/${channelId}`, null, 'DELETE'),
getChannel: channelOrThreadId => apiCall(`/channels/${channelOrThreadId}`),
pinnedMessages: channelId => apiCall(`/channels/${channelId}/pins`),
addPin: (channelId, messageId) => apiCall(`/channels/${channelId}/pins/${messageId}`, null, 'PUT'),
deletePin: (channelId, messageId) => apiCall(`/channels/${channelId}/pins/${messageId}`, null, 'DELETE'),
listEmojis: guildId => apiCall(`/guilds/${guildId}/emojis`),
getEmoji: (guildId, emojiId) => apiCall(`/guilds/${guildId}/emojis/${emojiId}`),
createEmoji: (guildId, name, image, roles) => apiCall(`/guilds/${guildId}`, { name, image, roles }, 'POST'),
editEmoji: (guildId, emojiId, name, roles) => apiCall(`/guilds/${guildId}/${emojiId}`, { name, roles }, 'PATCH'),
deleteEmoji: (guildId, emojiId) => apiCall(`/guilds/${guildId}/${emojiId}`, null, 'DELETE'),
getGuildCommandsAndApplications: guildId => apiCall(`/guilds/${guildId}/application-command-index`),
searchSlashCommands: async (guildId, searchWord = '') => {
const contextData = await apiCall(`/guilds/${guildId}/application-command-index`)
const commands = contextData.application_commands.filter(cmd => cmd.name.includes(searchWord))
if (contextData.application_commands?.length > 0 && commands.length === 0) {
throw new Error(`Command '${searchWord}' not found.`)
}
return commands
},
sendSlashCommand: (guildId, channelOrThreadId, command, commandOptions = []) => {
const formData = new FormData()
formData.append(
'payload_json',
JSON.stringify({
type: 2,
application_id: command.application_id,
guild_id: guildId,
channel_id: channelOrThreadId,
session_id: 'requiredButUnchecked',
nonce: Math.floor(Math.random() * 1000000) + '',
data: {
...command,
options: commandOptions,
application_command: {
...command,
},
},
}),
)
return apiCall('/interactions', formData, 'POST')
},
changeNick: (guildId, nick) => apiCall(`/guilds/${guildId}/members/@me/nick`, { nick }, 'PATCH'),
leaveServer: guildId => apiCall(`/users/@me/guilds/${guildId}`, null, 'DELETE'),
getServers: () => apiCall(`/users/@me/guilds`),
getGuilds: () => apiCall(`/users/@me/guilds`),
listCurrentUserGuilds: () => apiCall('/users/@me/guilds'),
getDMs: () => apiCall(`/users/@me/channels`),
getUser: userId => apiCall(`/users/${userId}`),
getDirectFriendInviteLinks: () => apiCall(`/users/@me/invites`),
createDirectFriendInviteLink: () => apiCall(`/users/@me/invites`, null, 'POST'),
deleteDirectFriendInviteLinks: () => apiCall(`/users/@me/invites`, null, 'DELETE'),
getCurrentUser: () => apiCall('/users/@me'),
editCurrentUser: (username, bio, body = {}) => apiCall('/users/@me', { username: username ?? undefined, bio: bio ?? undefined, ...body }, 'PATCH'),
setCustomStatus: (emojiId, emojiName, expiresAt, text) =>
apiCall(`/users/@me/settings`, { custom_status: { emoji_id: emojiId, emoji_name: emojiName, expires_at: expiresAt, text: text } }, 'PATCH'),
deleteCustomStatus: () => apiCall(`/users/@me/settings`, { custom_status: { expires_at: new Date().toJSON() } }, 'PATCH'),
listReactions: (channelOrThreadId, messageId, emojiUrl) => apiCall(`/channels/${channelOrThreadId}/messages/${messageId}/reactions/${emojiUrl}/@me`),
addReaction: (channelOrThreadId, messageId, emojiUrl) => apiCall(`/channels/${channelOrThreadId}/messages/${messageId}/reactions/${emojiUrl}/@me`, null, 'PUT'),
deleteReaction: (channelOrThreadId, messageId, emojiUrl) => apiCall(`/channels/${channelOrThreadId}/messages/${messageId}/reactions/${emojiUrl}/@me`, null, 'DELETE'),
typing: channelOrThreadId => apiCall(`/channels/${channelOrThreadId}/typing`, null, 'POST'),
delay,
downloadFileByUrl: (url, filename) =>
fetch(url)
.then(response => response.blob())
.then(blob => {
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
})
.catch(console.error),
apiCall,
id,
update_guildId_and_channelId_withCurrentlyVisible,
getConfig: () => Object.freeze({ authHeader, autoUpdateToken, guildId: gid, channelId: cid, gid, cid }),
setConfigAuthHeader: token => (authHeader = token),
setConfigAutoUpdateToken: bool => (autoUpdateToken = bool),
setConfigGid: id => (gid = id),
setConfigGuildId: id => (gid = id),
setConfigCid: id => (cid = id),
setConfigChannelId: id => (cid = id),
}
console.log('\n\n\n\nSelfbot loaded! Use it like this: `await api.someFunction()`')
console.log('Abusing this could get you banned from Discord, use at your own risk!')
console.log()
console.log(
'This script does **not** work with bot accounts! ' +
'If you have a bot account, use Node.js (or a proper lib like discord.js!) with the modified script ' +
'https://github.com/rigwild/discord-self-bot-console/discussions/4#discussioncomment-1438231',
)
console.log()
console.log('Use the `id()` function to update the variable `gid` guild id and `cid` channel id to what you are currently watching.')
console.log('https://github.com/rigwild/discord-self-bot-console')
id(false)
// Do not replace configuration when reusing script in same context
// @ts-ignore
if (!authHeader) {
//
// Set your authorization token HERE (or use the auto update, send a message in any chat!)
//
authHeader = ''
autoUpdateToken = true
}
// @ts-ignore
if (!XMLHttpRequest_setRequestHeader) {
var XMLHttpRequest_setRequestHeader = XMLHttpRequest.prototype.setRequestHeader
}
// Auto update the authHeader when a request with the token is intercepted
XMLHttpRequest.prototype.setRequestHeader = function () {
if (autoUpdateToken && arguments[0] === 'Authorization' && authHeader !== arguments[1]) {
authHeader = arguments[1]
console.log(`Updated the Auth token! <${authHeader.slice(0, 30)}...>`)
}
XMLHttpRequest_setRequestHeader.apply(this, arguments)
}
if (!module) {
// Allow pasting this script in the console
// @ts-ignore
var module = {}
}
module.exports = { api, id, delay, update_guildId_and_channelId_withCurrentlyVisible }
}