🚧 WIP for websocket connection

This commit is contained in:
Iván Ovejero 2021-03-05 18:31:29 -03:00
parent dacf719a24
commit 8b74713fe9
4 changed files with 106 additions and 10 deletions

View file

@ -11,6 +11,7 @@ import {
import { import {
discordApiRequest, discordApiRequest,
setHeartbeatInterval,
} from './GenericFunctions'; } from './GenericFunctions';
import { import {
@ -20,6 +21,12 @@ import {
userOperations, userOperations,
} from './descriptions'; } from './descriptions';
import WebSocket = require('ws');
import {
clearInterval,
} from 'timers';
export class Discord implements INodeType { export class Discord implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
displayName: 'Discord', displayName: 'Discord',
@ -75,6 +82,44 @@ export class Discord implements INodeType {
let responseData; let responseData;
const returnData: IDataObject[] = []; const returnData: IDataObject[] = [];
const { url } = await discordApiRequest.call(this, 'GET', '/gateway');
const ws = new WebSocket(`${url}?v=8&encoding=json`);
await new Promise((resolve) => {
ws.on('open', () => {
console.log('*** WEBSOCKET CONNECTION OPEN ***');
});
let heartbeatInterval: NodeJS.Timeout;
ws.on('message', (data: string) => {
const op = JSON.parse(data).op;
const mapping: { [key: number]: 'hello' | 'acknowledgment' } = {
10: 'hello',
11: 'acknowledgment',
};
console.log(`--- MESSAGE RECEIVED: ${mapping[op]} ---`);
if (mapping[op] === 'hello') {
console.log('--- SET HEARTBEAT ---');
heartbeatInterval = setHeartbeatInterval(ws, data);
resolve();
}
// TODO close connection if heartbeat acknowledgment does not arrive
console.log(`--- MESSAGE CONTENTS FOR ${mapping[op]} ---`);
console.log(JSON.parse(data));
});
ws.on('close', () => {
console.log('*** WEBSOCKET CONNECTION CLOSED ***');
console.log('--- UNSET HEARTBEAT ---');
clearInterval(heartbeatInterval);
});
});
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
if (resource === 'message') { if (resource === 'message') {
@ -104,7 +149,31 @@ export class Discord implements INodeType {
await discordApiRequest.call(this, 'POST', '', body, {}, webhookUrl); await discordApiRequest.call(this, 'POST', '', body, {}, webhookUrl);
responseData = { success: true }; responseData = { success: true };
} else if (operation === 'ws') {
const { oauthTokenData } = this.getCredentials('discordOAuth2Api') as {
oauthTokenData: { access_token: string }
};
const payload = JSON.stringify({
op: 2,
d: {
token: oauthTokenData.access_token,
intents: 513,
properties: {
$os: 'linux',
$browser: 'firefox',
$device: 'n8n',
},
},
});
// ws.send(payload);
} }
} }
if (resource === 'user') { if (resource === 'user') {
@ -136,6 +205,15 @@ export class Discord implements INodeType {
: returnData.push(responseData); : returnData.push(responseData);
} }
await new Promise(resolve => {
setTimeout(resolve, 3 * 60 * 1000);
});
ws.terminate();
// console.log(ws.);
return [this.helpers.returnJsonArray(returnData)]; return [this.helpers.returnJsonArray(returnData)];
} }
} }

View file

@ -12,6 +12,8 @@ import {
IDataObject, IDataObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
import WebSocket = require('ws');
export async function discordApiRequest( export async function discordApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions, this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions,
method: string, method: string,
@ -21,9 +23,15 @@ export async function discordApiRequest(
uri?: string, uri?: string,
option: IDataObject = {}, option: IDataObject = {},
) { ) {
const { oauthTokenData } = this.getCredentials('discordOAuth2Api') as {
oauthTokenData: { access_token: string }
};
const options: OptionsWithUri = { const options: OptionsWithUri = {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Authorization: `Bearer ${oauthTokenData.access_token}`,
}, },
method, method,
body, body,
@ -49,20 +57,24 @@ export async function discordApiRequest(
}; };
try { try {
console.log(options); // console.log(options);
return await this.helpers.requestOAuth2!.call(this, 'discordOAuth2Api', options, oAuth2Options); return await this.helpers.requestOAuth2!.call(this, 'discordOAuth2Api', options, oAuth2Options);
// return await this.helpers.request!.call(this, options);
} catch (error) { } catch (error) {
const errors = error.response.body.context_info.errors; // TODO
const message = error.response.body.message;
if (errors) {
const errorMessage = errors.map((e: IDataObject) => e.message).join('|');
throw new Error(`Discord error response [${error.statusCode}]: ${errorMessage}`);
} else if (message) {
throw new Error(`Discord error response [${error.statusCode}]: ${message}`);
}
throw error; throw error;
} }
} }
export function setHeartbeatInterval(ws: WebSocket, data: string) {
const interval = JSON.parse(data).d.heartbeat_interval;
const payload = JSON.stringify({
op: 1, // opcode
d: 251, // last sequence number `s` received by client
});
return setInterval(() => ws.send(payload), interval);
}

View file

@ -20,6 +20,10 @@ export const messageOperations = [
value: 'create', value: 'create',
description: 'Create a message', description: 'Create a message',
}, },
{
name: 'WS',
value: 'ws',
},
], ],
default: 'create', default: 'create',
description: 'The operation to perform.', description: 'The operation to perform.',

View file

@ -555,6 +555,7 @@
"@types/ssh2-sftp-client": "^5.1.0", "@types/ssh2-sftp-client": "^5.1.0",
"@types/tmp": "^0.2.0", "@types/tmp": "^0.2.0",
"@types/uuid": "^3.4.6", "@types/uuid": "^3.4.6",
"@types/ws": "^7.4.0",
"@types/xml2js": "^0.4.3", "@types/xml2js": "^0.4.3",
"gulp": "^4.0.0", "gulp": "^4.0.0",
"jest": "^26.4.2", "jest": "^26.4.2",
@ -609,6 +610,7 @@
"tmp-promise": "^3.0.2", "tmp-promise": "^3.0.2",
"uuid": "^3.4.0", "uuid": "^3.4.0",
"vm2": "^3.6.10", "vm2": "^3.6.10",
"ws": "^7.4.3",
"xlsx": "^0.16.7", "xlsx": "^0.16.7",
"xml2js": "^0.4.22" "xml2js": "^0.4.22"
}, },