The code establishes and manages a WebSocket connection to Discord, handling events such as connection opening, message receiving, and connection closing, with functions like authorizeGateway()
and closeGateway()
. It also includes functions like requestAuthQ()
for making requests to the Discord API, although this function appears to be incomplete and intended to handle a queue of requests.
npm run import -- "discord request"
const {request} = require('gaxios')
const WebSocket = require('ws')
const {delay, wait, timeout} = importer.import("discord utilities")
const {
gatewayIdentified, gatewayClose, gatewayMessage,
} = importer.import("discord gateway")
const {
TOKEN, DEFAULT_API, DEFAULT_RATE
} = importer.import("discord configuration")
var ws = false
var wsConnecting = false
var previousRequest = 0
async function gatewayUrl() {
// TODO: return the same result if queried less than 1 second ago
// doesn't use requestAuthQ because that would create an infinite loop
var result = await request({
headers: {
'Authorization': `Bot ${TOKEN}`
},
method: 'GET',
url: `${DEFAULT_API}gateway/bot`
})
return result.data
}
function gatewayOpen() {
console.log('Connecting to Discord')
}
async function authorizeGateway() {
if(wsConnecting) {
var result = await wait(() => ws && ws.identified, 3000)
if(!result)
return await authorizeGateway()
else
return ws
} else if (ws && ws.readyState == 1 && ws.identified) {
return ws
}
wsConnecting = true
try {
ws = new WebSocket((await gatewayUrl()).url)
ws.identified = false
} catch (e) {
console.log('Authorize error', e.message)
ws = false
wsConnecting = false
return
}
ws.on('open', gatewayOpen)
ws.on('message', gatewayMessage.bind(null, ws, authorizeGateway, interactionResponse))
ws.on('close', gatewayClose.bind(null, ws, authorizeGateway))
await wait(() => ws.identified, 3000)
wsConnecting = false
return ws
}
function closeGateway() {
gatewayClose(ws)
}
var previousRequest = 0
async function requestAuthQ(outgoing) {
await authorizeGateway()
if(typeof outgoing.headers == 'undefined')
outgoing.headers = {}
outgoing.headers['Authorization'] = `Bot ${TOKEN}`
outgoing.url = DEFAULT_API + outgoing.url
previousRequest = await delay(previousRequest, DEFAULT_RATE)
var resolveRequest
resolveRequest = async () => {
var result
try {
//console.log('Discord request', outgoing)
result = (await request(outgoing)).data
} catch (e) {
// check result for rate limit and re-run this request in a queue
if(e.code == 429) {
console.log('Delayed request', e.response.data.retry_after)
await timeout(e.response.data.retry_after * 1000)
return await resolveRequest()
} else {
console.log(e)
if(e.response) {
console.log(e.response.data.errors)
}
throw e
}
}
return result
}
return await resolveRequest()
}
async function interactionResponse(interactionId, interactionToken) {
var json = {
'type': 5
}
return await requestAuthQ({
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
url: `interactions/${interactionId}/${interactionToken}/callback`,
data: JSON.stringify(json)
})
}
module.exports = {
authorizeGateway,
gatewayUrl,
closeGateway,
requestAuthQ,
interactionResponse,
}
const { request } = require('gaxios');
const WebSocket = require('ws');
const { delay, wait, timeout } = require('discord utilities');
const {
gatewayIdentified, gatewayClose, gatewayMessage,
} = require('discord gateway');
const {
TOKEN, DEFAULT_API, DEFAULT_RATE
} = require('discord configuration');
class DiscordGateway {
constructor() {
this.ws = false;
this.wsConnecting = false;
this.previousRequest = 0;
}
async gatewayUrl() {
try {
const result = await request({
headers: {
Authorization: `Bot ${TOKEN}`,
},
method: 'GET',
url: `${DEFAULT_API}gateway/bot`,
});
return result.data;
} catch (error) {
console.error('Error getting gateway URL:', error);
throw error;
}
}
async authorizeGateway() {
const now = Date.now();
if (this.wsConnecting && now - this.previousRequest < 1000) {
// Return cached result from previous request if queried less than 1 second ago
return this.ws;
}
this.wsConnecting = true;
try {
const result = await this.gatewayUrl();
this.ws = new WebSocket(result.url);
this.ws.identified = false;
} catch (error) {
console.error('Authorize error:', error.message);
this.ws = false;
this.wsConnecting = false;
return;
}
this.ws.on('open', () => console.log('Connecting to Discord'));
this.ws.on('message', (event) => gatewayMessage(event, this.authorizeGateway));
this.ws.on('close', () => gatewayClose(this.ws, this.authorizeGateway));
await this.waitForGatewayIdentification(3000);
this.wsConnecting = false;
return this.ws;
}
async waitForGatewayIdentification(timeoutMs) {
if (this.ws && this.ws.readyState === 1 && this.ws.identified) {
return this.ws;
}
try {
await wait(() => this.ws.identified, timeoutMs);
} catch (error) {
console.error('Error waiting for gateway identification:', error);
throw error;
}
}
async closeGateway() {
gatewayClose(this.ws, this.authorizeGateway);
}
async requestAuthQ(outgoing) {
await this.authorizeGateway();
if (!outgoing.headers) {
outgoing.headers = {};
}
outgoing.headers.Authorization = `Bot ${TOKEN}`;
outgoing.url = DEFAULT_API + outgoing.url;
const waitTime = await delay(this.previousRequest, DEFAULT_RATE);
this.previousRequest = waitTime;
const resolveRequest = async () => {
try {
const result = await request(outgoing);
return result.data;
} catch (error) {
if (error.code === 429) {
console.log('Delayed request:', error.response.data.retry_after);
await timeout(error.response.data.retry_after * 1000);
return await resolveRequest();
} else {
console.error(error);
if (error.response) {
console.error(error.response.data.errors);
}
throw error;
}
}
};
return await resolveRequest();
}
async interactionResponse(interactionId, interactionToken) {
const json = { type: 5 };
return await this.requestAuthQ({
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
url: `interactions/${interactionId}/${interactionToken}/callback`,
data: JSON.stringify(json),
});
}
}
module.exports = new DiscordGateway();
The code imports various modules and variables:
gaxios
: a Google API client library for making HTTP requests.ws
: a WebSocket library for establishing a real-time connection to Discord.discord utilities
, discord gateway
, and discord configuration
: custom modules for handling Discord-related functionality.Variables are declared for the WebSocket connection (ws
and wsConnecting
) and for tracking the timing of requests (previousRequest
).
gatewayUrl()
This function retrieves the Discord gateway URL by making a GET request to the Discord API with the bot token as an authorization header. The result is returned as a promise.
gatewayOpen()
This function logs a message to the console indicating that the connection to Discord is being established.
authorizeGateway()
This function is responsible for establishing a WebSocket connection to Discord and authorizing the bot. It:
wsConnecting
) or if a connection already exists (ws
).gatewayUrl()
.open
: logs a message to the console when the connection is established.message
: receives messages from Discord and passes them to gatewayMessage()
for handling.close
: closes the connection and attempts to re-authorize when the connection is closed.closeGateway()
This function closes the WebSocket connection to Discord.
requestAuthQ()
This function makes a request to the Discord API with authentication. It:
authorizeGateway()
to establish a new connection if necessary.Note that this function seems to be incomplete, as indicated by the TODO comment and the presence of a resolveRequest
function. The code appears to be intended to handle a queue of requests, but this part is not fully implemented.