@jnode/discord 1.0.0 → 1.0.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jnode/discord",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Simple Discord API package for Node.js.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/client.js ADDED
@@ -0,0 +1,123 @@
1
+ /*
2
+ JustDiscord/client.js
3
+ v.1.0.0
4
+
5
+ Simple Discord API package for Node.js.
6
+ Must use Node.js v22.4.0 or later for WebSocket (Discord gateway).
7
+
8
+ by JustNode Dev Team / JustApple
9
+ */
10
+
11
+ //load jnode packages
12
+ const request = require('@jnode/request');
13
+
14
+ //load classes and functions
15
+ const DiscordGateway = require('./gateway.js');
16
+
17
+ //error types
18
+ //errors from Discord API
19
+ class DiscordAPIError extends Error {
20
+ constructor(code, body, headers) {
21
+ super(`Discord API respond with code ${code}.`);
22
+ this.body = body;
23
+ this.headers = headers;
24
+ }
25
+ }
26
+
27
+ //Discord Client, everything starts here
28
+ class DiscordClient {
29
+ constructor(token, options = {}) {
30
+ this.token = token;
31
+
32
+ //client api options
33
+ this.apiVersion = options.apiVersion ?? 10;
34
+ this.apiBase = options.apiBase ?? 'discord.com/api';
35
+ this.apiAutoRetry = options.apiAutoRetry ?? true;
36
+ this.apiThrowError = options.apiThrowError ?? true; //throw error while api status code is not 2xx
37
+
38
+ //client gateway options
39
+ this.gatewayIntents = options.gatewayIntents ?? 0b11111111111111111000110011;
40
+ this.gatewayUrl = options.gatewayUrl ?? 'wss://gateway.discord.gg';
41
+ this.gatewayOriginalUrl = this.gatewayUrl;
42
+ this.gatewayReconnectDelay = options.gatewayReconnectDelay ?? 5000; //less than 0 to disable auto reconnect
43
+ this.gatewayConnectTimeout = options.gatewayConnectTimeout ?? 5000; //less than 0 to disable connect timeout (may cause error)
44
+ this.gatewayThrowError = options.gatewayThrowError ?? true; //throw error while gateway went something wrong
45
+
46
+ //gateway
47
+ this.gateway = new DiscordGateway(this);
48
+ }
49
+
50
+ //get full api url with base, version and path
51
+ apiUrl(path) {
52
+ return `https://${this.apiBase}/v${this.apiVersion}${path}`;
53
+ }
54
+
55
+ //make an request to Discord
56
+ async apiRequest(method = 'GET', path = '/', body) {
57
+ const res = await request.request(method, this.apiUrl(path), {
58
+ 'Authorization': `Bot ${this.token}`,
59
+ 'User-Agent': 'DiscordBot',
60
+ 'Content-Type': (body !== undefined) ? 'application/json' : null
61
+ }, (body !== undefined) ? JSON.stringify(body) : undefined); //make an request
62
+
63
+ if ((res.code === 429) && this.apiAutoRetry) { //retry if recieved 429
64
+ await delay(res.json.retry_after);
65
+ return this.apiRequest(method, path, body);
66
+ }
67
+
68
+ if (((res.code > 299) || (res.code < 200)) && this.apiThrowError) { //throw error if not 2xx
69
+ throw new DiscordAPIError(res.code, res.json ?? res.body, res.headers);
70
+ }
71
+
72
+ return res;
73
+ }
74
+
75
+ //make an multi part request to Discord
76
+ async apiRequestMultipart(method = 'GET', path = '/', body, attachments = []) {
77
+ let parts = [];
78
+ parts.push({ //json data
79
+ disposition: 'name="payload_json"',
80
+ contentType: 'application/json',
81
+ data: JSON.stringify(body)
82
+ });
83
+
84
+ for (let i = 0; i < attachments.length; i++) { //add every attachment
85
+ parts.push({
86
+ disposition: `name="files[${i}]"; filename="${encodeURIComponent(attachments[i].name)}"`,
87
+ contentType: attachments[i].type,
88
+ data: attachments[i].data,
89
+ encoded: attachments[i].encoded
90
+ });
91
+ }
92
+
93
+ const res = await request(method, this.apiUrl(path), {
94
+ 'Authorization': `Bot ${this.token}`,
95
+ 'User-Agent': 'DiscordBot',
96
+ 'Content-Type': 'multipart/form-data; boundary=----JustDiscordFormBoundary'
97
+ }, request.generateMultipartBody(parts)); //make an request
98
+
99
+ if ((res.code === 429) && this.apiAutoRetry) { //retry if recieved 429
100
+ await delay(res.json.retry_after);
101
+ return this.apiRequest(method, path, body, multipart, true);
102
+ }
103
+
104
+ if (((res.code > 299) || (res.code < 200)) && this.apiThrowError) { //throw error if not 2xx
105
+ throw new DiscordAPIError(res.code, res.json ?? res.body, res.headers);
106
+ }
107
+
108
+ return res;
109
+ }
110
+
111
+ async connectGateway() {
112
+ await this.gateway.getGatewayUrl();
113
+ this.gateway.connect();
114
+ return this.gateway;
115
+ }
116
+ }
117
+
118
+ //wait time (ms)
119
+ function delay(ms) {
120
+ return new Promise((resolve, reject) => { setTimeout(resolve, ms); });
121
+ }
122
+
123
+ module.exports = DiscordClient;
package/src/gateway.js ADDED
@@ -0,0 +1,151 @@
1
+ /*
2
+ JustDiscord/gateway.js
3
+ v.1.0.0
4
+
5
+ Simple Discord API package for Node.js.
6
+ Must use Node.js v22.4.0 or later for WebSocket (Discord gateway).
7
+
8
+ by JustNode Dev Team / JustApple
9
+ */
10
+
11
+ //load classes and functions
12
+ //errors from Discord Gateway
13
+ class DiscordGatewayError extends Error { constructor(...i) { super(...i); } }
14
+
15
+ //load node packages
16
+ const EventEmitter = require('events');
17
+
18
+ //Discord Gateway, recieve live messages with WebSocket
19
+ class DiscordGateway extends EventEmitter {
20
+ constructor(client) {
21
+ super();
22
+
23
+ this.client = client;
24
+ this.socket = {};
25
+ this.s = null;
26
+ }
27
+
28
+ //get gateway url from api
29
+ async getGatewayUrl() {
30
+ const res = await this.client.apiRequest('GET', '/gateway/bot'); //make an api request
31
+ this.client.gatewayUrl = res.json.url;
32
+ this.client.gatewayOriginalUrl = res.json.url;
33
+ return res.json.url;
34
+ }
35
+
36
+ //connect to gateway
37
+ connect() {
38
+ if ((this.socket.readyState !== 3) && this.socket.close) this.socket.close(); //close privous connect
39
+
40
+ this.socket = new WebSocket(`${this.client.gatewayUrl}?v=${this.client.apiVersion}&encoding=json`); //connect
41
+
42
+ if (this.client.gatewayConnectTimeout !== 0) { //set connect timeout
43
+ this.connectTimeout = setTimeout(() => {
44
+ this.socket.close(); //close socket
45
+ this.emit('timeout');
46
+
47
+ if (this.client.gatewayReconnectDelay >= 0) { //if auto reconnect is allowed
48
+ setTimeout(() => {
49
+ this.connect();
50
+ }, this.client.gatewayReconnectDelay); //reconnect after specific time
51
+ }
52
+ }, this.client.gatewayConnectTimeout);
53
+ }
54
+
55
+ this.socket.addEventListener('open', (event) => { //socket opened
56
+ this.emit('socketOpened', event); //send event with socket event
57
+ clearTimeout(this.connectTimeout); //clear timeout if successfully connected
58
+ });
59
+
60
+ this.socket.addEventListener('close', (event) => { //socket closed
61
+ this.emit('socketClosed', event); //send event with event (may have close code and reason)
62
+ clearInterval(this.heartbeatInterval); //clear heatbeat interval
63
+
64
+
65
+ if (this.client.gatewayReconnectDelay >= 0) { //if auto reconnect is allowed
66
+ setTimeout(() => {
67
+ this.connect();
68
+ }, this.client.gatewayReconnectDelay); //reconnect after specific time
69
+ }
70
+ });
71
+
72
+ this.socket.addEventListener('error', (event) => { //socket error
73
+ this.emit('socketError', event); //send event with event (may have error info)
74
+ });
75
+
76
+ this.socket.addEventListener('message', (event) => { //recieve gateway message
77
+ try {
78
+ this.emit('socketMessage', event); //send event with event (may have data and more)
79
+
80
+ const data = JSON.parse(event.data); //parse json data
81
+ this.emit('message', data); //send event with json data
82
+
83
+ //auto handle by op
84
+ switch (data.op) {
85
+ case 0: //Dispatch
86
+ this.emit(data.t, data.d); //send t event with d
87
+ this.s = data.s ?? this.s; //save s number there is
88
+
89
+ //auto handle by t
90
+ switch (data.t) {
91
+ case 'READY': //save resume data
92
+ this.sessionId = data.d['session_id'];
93
+ this.client.gatewayUrl = data.d['resume_gateway_url'];
94
+ break;
95
+ default:
96
+ break;
97
+ }
98
+ break;
99
+ case 7: //Reconnect, need to reconnect
100
+ this.socket.close(); //close socket
101
+ break;
102
+ case 9: //Invalid Session, need to reconnect
103
+ this.client.gatewayUrl = this.client.gatewayOriginalUrl; //reset url
104
+ this.sessionId = undefined; //reset session id
105
+ this.socket.close(); //close socket
106
+ break;
107
+ case 10: //Hello
108
+ this.heartbeatIntervalMS = data.d['heartbeat_interval'] * 0.9; //save heart beat interval time
109
+ this.sendMessage(1); //send first heartbeat
110
+
111
+ clearInterval(this.heartbeatInterval); //clear old interval
112
+ this.heartbeatInterval = setInterval(() => { //start heartbeat interval
113
+ this.sendMessage(1);
114
+ }, this.heartbeatIntervalMS);
115
+
116
+ if (this.sessionId) { //may be resumed
117
+ this.sendMessage(6, { //Resume
118
+ token: this.client.token,
119
+ session_id: this.sessionId,
120
+ seq: this.s
121
+ });
122
+ } else { //start new session
123
+ this.sendMessage(2, { //Identify
124
+ token: this.client.token,
125
+ properties: {
126
+ os: process.platform,
127
+ browser: 'Node.js',
128
+ device: 'JustDiscord'
129
+ },
130
+ intents: this.client.gatewayIntents
131
+ });
132
+ }
133
+ break;
134
+ default:
135
+ break;
136
+ }
137
+ } catch (err) {
138
+ if (this.client.gatewayThrowError) { //throw gateway error
139
+ err.name = 'DiscordGatewayError'; //change the name of the error
140
+ throw err;
141
+ }
142
+ }
143
+ });
144
+ }
145
+
146
+ sendMessage(op, d = null) {
147
+ this.socket.send(JSON.stringify({ op, d }));
148
+ }
149
+ }
150
+
151
+ module.exports = DiscordGateway;
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- JustDiscord
2
+ JustDiscord/index.js
3
3
  v.1.0.0
4
4
 
5
5
  Simple Discord API package for Node.js.
@@ -8,4 +8,10 @@ Must use Node.js v22.4.0 or later for WebSocket (Discord gateway).
8
8
  by JustNode Dev Team / JustApple
9
9
  */
10
10
 
11
- require('@jnode/request')
11
+ //load node packages
12
+ const EventEmitter = require('events');
13
+
14
+ module.exports = {
15
+ client: require('./client.js'),
16
+ gateway: require('./gateway.js')
17
+ };