websocket thingy
This commit is contained in:
parent
09c4d462ed
commit
66232da6f8
11 changed files with 195 additions and 6 deletions
|
@ -14,8 +14,10 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
|
"@types/ws": "^8.2.2",
|
||||||
"dotenv": "^14.2.0",
|
"dotenv": "^14.2.0",
|
||||||
"express": "^4.17.2",
|
"express": "^4.17.2",
|
||||||
"log75": "^2.2.0"
|
"log75": "^2.2.0",
|
||||||
|
"ws": "^8.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { config } from 'dotenv';
|
import { config } from 'dotenv';
|
||||||
import Express, { Request, Response } from "express";
|
import Express from "express";
|
||||||
import Log75, { LogLevel } from 'log75';
|
import Log75, { LogLevel } from 'log75';
|
||||||
|
|
||||||
config();
|
config();
|
||||||
|
@ -10,8 +10,15 @@ const DEBUG = process.env.NODE_ENV != 'production';
|
||||||
const logger: Log75 = new (Log75 as any).default(DEBUG ? LogLevel.Debug : LogLevel.Standard);
|
const logger: Log75 = new (Log75 as any).default(DEBUG ? LogLevel.Debug : LogLevel.Standard);
|
||||||
const app = Express();
|
const app = Express();
|
||||||
|
|
||||||
app.get('/', (req: Request, res: Response) => {
|
export { logger, app, PORT }
|
||||||
res.send({ msg: "yo" });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(PORT, () => logger.info(`Listening on port ${PORT}`));
|
(async () => {
|
||||||
|
await Promise.all([
|
||||||
|
import('./middlewares/log'),
|
||||||
|
import('./routes/internal/ws'),
|
||||||
|
import('./routes/root'),
|
||||||
|
]);
|
||||||
|
logger.done('All routes and middlewares loaded');
|
||||||
|
})();
|
||||||
|
|
||||||
|
import('./server');
|
||||||
|
|
7
api/src/middlewares/log.ts
Normal file
7
api/src/middlewares/log.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import { app, logger } from "..";
|
||||||
|
|
||||||
|
app.use('*', (req: Request, res: Response, next: () => void) => {
|
||||||
|
logger.debug(`${req.method} ${req.path}`);
|
||||||
|
next();
|
||||||
|
});
|
53
api/src/routes/internal/ws.ts
Normal file
53
api/src/routes/internal/ws.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* Provides a WebSocket the bot can connect to.
|
||||||
|
* (IPC on crack)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { WebSocketServer, WebSocket } from 'ws';
|
||||||
|
import { logger } from "../..";
|
||||||
|
import server from '../../server';
|
||||||
|
if (!process.env.BOT_API_TOKEN) {
|
||||||
|
logger.error(`$BOT_API_TOKEN is not set. This token is `
|
||||||
|
+ `required for the bot to communicate with the API.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { BOT_API_TOKEN } = process.env;
|
||||||
|
const wsServer = new WebSocketServer({ noServer: true });
|
||||||
|
const sockets: WebSocket[] = [];
|
||||||
|
|
||||||
|
wsServer.on('connection', (sock) => {
|
||||||
|
sockets.push(sock);
|
||||||
|
|
||||||
|
sock.once('close', () => {
|
||||||
|
logger.debug('WS closed');
|
||||||
|
const i = sockets.findIndex(s => s == sock);
|
||||||
|
sockets.splice(i, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.on('message', (msg) => {
|
||||||
|
logger.debug(`[WS] [<] ${msg.toString()}`);
|
||||||
|
sock.send(JSON.stringify({ "h": JSON.parse(msg.toString()) }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('upgrade', (req, socket, head) => {
|
||||||
|
logger.debug(`WS Upgrade ${req.url}`);
|
||||||
|
|
||||||
|
switch(req.url) {
|
||||||
|
case '/internal/ws':
|
||||||
|
if (req.headers['authorization'] !== BOT_API_TOKEN) {
|
||||||
|
logger.debug('WS unauthorized');
|
||||||
|
head.write(JSON.stringify({ error: 'Not authenticated' }, null, 4));
|
||||||
|
socket.end();
|
||||||
|
} else {
|
||||||
|
wsServer.handleUpgrade(req, socket, head, (sock) => {
|
||||||
|
wsServer.emit('connection', sock, req);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
head.write(JSON.stringify({ error: 'Cannot open WebSocket on this endpoint' }, null, 4));
|
||||||
|
socket.end();
|
||||||
|
}
|
||||||
|
});
|
6
api/src/routes/root.ts
Normal file
6
api/src/routes/root.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { app } from '..';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
|
||||||
|
app.get('/', (req: Request, res: Response) => {
|
||||||
|
res.send({ msg: "yo" });
|
||||||
|
});
|
5
api/src/server.ts
Normal file
5
api/src/server.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { app, logger, PORT } from ".";
|
||||||
|
|
||||||
|
const server = app.listen(PORT, () => logger.info(`Listening on port ${PORT}`));
|
||||||
|
|
||||||
|
export default server;
|
|
@ -64,6 +64,13 @@
|
||||||
"@types/mime" "^1"
|
"@types/mime" "^1"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/ws@^8.2.2":
|
||||||
|
version "8.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21"
|
||||||
|
integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
accepts@~1.3.7:
|
accepts@~1.3.7:
|
||||||
version "1.3.7"
|
version "1.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
||||||
|
@ -429,3 +436,8 @@ vary@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||||
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
|
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
|
||||||
|
|
||||||
|
ws@^8.4.2:
|
||||||
|
version "8.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.2.tgz#18e749868d8439f2268368829042894b6907aa0b"
|
||||||
|
integrity sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==
|
||||||
|
|
70
bot/src/bot/modules/api_communication.ts
Normal file
70
bot/src/bot/modules/api_communication.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* This handles communication with the API server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ws from "ws";
|
||||||
|
import logger from "../logger";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
|
const wsEvents = new EventEmitter();
|
||||||
|
const { API_WS_URL, API_WS_TOKEN } = process.env;
|
||||||
|
const wsQueue: { [key: string]: string }[] = [];
|
||||||
|
let client: ws|undefined = undefined;
|
||||||
|
|
||||||
|
if (!API_WS_URL || !API_WS_TOKEN)
|
||||||
|
logger.info("$API_WS_URL or $API_WS_TOKEN not found.");
|
||||||
|
else {
|
||||||
|
logger.info(`$API_WS_URL and $API_WS_TOKEN set; Connecting to ${API_WS_URL}`);
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
if (client && client.readyState == ws.OPEN) client.close();
|
||||||
|
client = new ws(API_WS_URL!, { headers: { authorization: API_WS_TOKEN! } });
|
||||||
|
|
||||||
|
client.once("open", () => {
|
||||||
|
logger.debug("WS connected");
|
||||||
|
if (wsQueue.length > 0) {
|
||||||
|
logger.debug(`Attempting to send ${wsQueue.length} queued WS messages`);
|
||||||
|
|
||||||
|
while (wsQueue.length > 0) {
|
||||||
|
if (client?.readyState != ws.OPEN) break;
|
||||||
|
const data = JSON.stringify(wsQueue.shift());
|
||||||
|
logger.debug(`[WS] [FROM QUEUE] [>] ${data}`);
|
||||||
|
client.send(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.once("close", () => {
|
||||||
|
client = undefined;
|
||||||
|
logger.warn(`WS closed, reconnecting in 3 seconds`);
|
||||||
|
setTimeout(connect, 3000);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.once('error', (err) => {
|
||||||
|
client = undefined;
|
||||||
|
logger.warn(`WS: ${err}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('message', (msg) => {
|
||||||
|
logger.debug(`[WS] [<] ${msg.toString('utf8')}`);
|
||||||
|
try {
|
||||||
|
wsEvents.emit('message', JSON.parse(msg.toString('utf8')));
|
||||||
|
} catch(e) { console.error(e) }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function wsSend(data: { [key: string]: string }) {
|
||||||
|
if (client && client.readyState == client.OPEN) {
|
||||||
|
logger.debug(`[WS] [>] ${JSON.stringify(data)}`);
|
||||||
|
client.send(JSON.stringify(data));
|
||||||
|
} else {
|
||||||
|
logger.debug(`[WS] [QUEUED] [>] ${JSON.stringify(data)}`);
|
||||||
|
wsQueue.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(() => wsSend({ "among": "us" }), 1000);
|
||||||
|
|
||||||
|
export { wsEvents, wsSend }
|
|
@ -25,4 +25,5 @@ export { client }
|
||||||
import('./bot/modules/event_handler');
|
import('./bot/modules/event_handler');
|
||||||
import('./bot/modules/tempbans');
|
import('./bot/modules/tempbans');
|
||||||
import('./bot/modules/user_scan');
|
import('./bot/modules/user_scan');
|
||||||
|
import('./bot/modules/api_communication');
|
||||||
})();
|
})();
|
||||||
|
|
6
package.json
Normal file
6
package.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@types/ws": "^8.2.2",
|
||||||
|
"ws": "^8.4.2"
|
||||||
|
}
|
||||||
|
}
|
20
yarn.lock
Normal file
20
yarn.lock
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@types/node@*":
|
||||||
|
version "17.0.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.10.tgz#616f16e9d3a2a3d618136b1be244315d95bd7cab"
|
||||||
|
integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==
|
||||||
|
|
||||||
|
"@types/ws@^8.2.2":
|
||||||
|
version "8.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21"
|
||||||
|
integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
ws@^8.4.2:
|
||||||
|
version "8.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.2.tgz#18e749868d8439f2268368829042894b6907aa0b"
|
||||||
|
integrity sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==
|
Loading…
Reference in a new issue