From e37f6aad08fa3bb76c65af68d54853040d343adc Mon Sep 17 00:00:00 2001 From: Levent007 Date: Thu, 4 Mar 2021 23:36:57 +0100 Subject: [PATCH] Squad IO API Plugin --- README.md | 503 ++++++++++++++------------- config.json | 160 +++++---- squad-server/layers/layers.js | 21 +- squad-server/package.json | 1 + squad-server/plugins/squad-io-api.js | 133 +++++++ 5 files changed, 482 insertions(+), 336 deletions(-) create mode 100644 squad-server/plugins/squad-io-api.js diff --git a/README.md b/README.md index 8b717c82..c92cfe89 100644 --- a/README.md +++ b/README.md @@ -268,44 +268,111 @@ The following is a list of plugins built into SquadJS, you can click their title Interested in creating your own plugin? [See more here](./squad-server/plugins/readme.md)
- DiscordPlaceholder -

DiscordPlaceholder

-

The DiscordPlaceholder plugin allows you to make your bot create placeholder messages that can be used when configuring other plugins.

+ AutoKickUnassigned +

AutoKickUnassigned

+

The AutoKickUnassigned plugin will automatically kick players that are not in a squad after a specified ammount of time.

Options

-
- DiscordSubsystemRestarter -

DiscordSubsystemRestarter

-

The DiscordSubSystemRestarter plugin allows you to manually restart SquadJS subsystems in case an issues arises with them.

+ AutoTKWarn +

AutoTKWarn

+

The AutoTkWarn plugin will automatically warn players with a message when they teamkill.

Options

-
+ +
+ ChatCommands +

ChatCommands

+

The ChatCommands plugin can be configured to make chat commands that broadcast or warn the caller with present messages.

+

Options

+ +
[
+  {
+    "command": "squadjs",
+    "type": "warn",
+    "response": "This server is powered by SquadJS.",
+    "ignoreChats": []
+  }
+]
- DiscordChat -

DiscordChat

-

The DiscordChat plugin will log in-game chat to a Discord channel.

+ DBLog +

DBLog

+

The mysql-log plugin will log various server statistics and events to a database. This is great for server performance monitoring and/or player stat tracking. + +Grafana (NOT YET WORKING WITH V2): +

+

Options

+ +
+ +
+ DiscordAdminBroadcast +

DiscordAdminBroadcast

+

The DiscordAdminBroadcast plugin will send a copy of admin broadcasts made in game to a Discord channel.

Options

+
16761867
- DiscordAdminBroadcast -

DiscordAdminBroadcast

-

The DiscordAdminBroadcast plugin will send a copy of admin broadcasts made in game to a Discord channel.

+ DiscordAdminCamLogs +

DiscordAdminCamLogs

+

The DiscordAdminCamLogs plugin will log in game admin camera usage to a Discord channel.

Options

- DiscordServerStatus -

DiscordServerStatus

-

The DiscordServerStatus plugin updates a message in Discord with current server information, e.g. player count.

+ DiscordChat +

DiscordChat

+

The DiscordChat plugin will log in-game chat to a Discord channel.

Options

-
- -
- AutoKickUnassigned -

AutoKickUnassigned

-

The AutoKickUnassigned plugin will automatically kick players that are not in a squad after a specified ammount of time.

-

Options

- +
[
+  "ChatSquad"
+]
@@ -527,29 +537,26 @@ Interested in creating your own plugin? [See more here](./squad-server/plugins/r
- IntervalledBroadcasts -

IntervalledBroadcasts

-

The IntervalledBroadcasts plugin allows you to set broadcasts, which will be broadcasted at preset intervals

+ DiscordPlaceholder +

DiscordPlaceholder

+

The DiscordPlaceholder plugin allows you to make your bot create placeholder messages that can be used when configuring other plugins.

Options

-
- DiscordTeamkill -

DiscordTeamkill

-

The DiscordTeamkill plugin logs teamkills and related information to a Discord channel for admins to review.

+ DiscordRcon +

DiscordRcon

+

The DiscordRcon plugin allows a specified Discord channel to be used as a RCON console to run RCON commands.

Options

-
- SeedingMode -

SeedingMode

-

The SeedingMode plugin broadcasts seeding rule messages to players at regular intervals when the server is below a specified player count. It can also be configured to display "Live" messages when the server goes live.

-

Options

- -
- -
- AutoTKWarn -

AutoTKWarn

-

The AutoTkWarn plugin will automatically warn players with a message when they teamkill.

-

Options

- -
- -
- DBLog -

DBLog

-

The mysql-log plugin will log various server statistics and events to a database. This is great for server performance monitoring and/or player stat tracking. - -Grafana (NOT YET WORKING WITH V2): -

-

Options

- -
-
DiscordRoundWinner

DiscordRoundWinner

@@ -671,51 +612,60 @@ Grafana (NOT YET WORKING WITH V2):
- ChatCommands -

ChatCommands

-

The ChatCommands plugin can be configured to make chat commands that broadcast or warn the caller with present messages.

+ DiscordServerStatus +

DiscordServerStatus

+

The DiscordServerStatus plugin updates a message in Discord with current server information, e.g. player count.

Options

-
- DiscordAdminCamLogs -

DiscordAdminCamLogs

-

The DiscordAdminCamLogs plugin will log in game admin camera usage to a Discord channel.

+ DiscordSubsystemRestarter +

DiscordSubsystemRestarter

+

The DiscordSubSystemRestarter plugin allows you to manually restart SquadJS subsystems in case an issues arises with them.

Options

+
667741905228136459
- DiscordRcon -

DiscordRcon

-

The DiscordRcon plugin allows a specified Discord channel to be used as a RCON console to run RCON commands.

+ DiscordTeamkill +

DiscordTeamkill

+

The DiscordTeamkill plugin logs teamkills and related information to a Discord channel for admins to review.

Options

- TeamRandomizer -

TeamRandomizer

-

The TeamRandomizer can be used to randomize teams. It's great for destroying clan stacks or for social events. It can be run by typing, by default, !randomize into in-game admin chat

+ IntervalledBroadcasts +

IntervalledBroadcasts

+

The IntervalledBroadcasts plugin allows you to set broadcasts, which will be broadcasted at preset intervals

Options

-
@@ -782,6 +733,74 @@ Grafana (NOT YET WORKING WITH V2):
6
+
+ SeedingMode +

SeedingMode

+

The SeedingMode plugin broadcasts seeding rule messages to players at regular intervals when the server is below a specified player count. It can also be configured to display "Live" messages when the server goes live.

+

Options

+ +
+ +
+ SquadIOAPI +

SquadIOAPI

+

The SquadIOAPI plugin allows remote access to a SquadJS instance via Socket.IO

+

Options

+ +
+ +
+ TeamRandomizer +

TeamRandomizer

+

The TeamRandomizer can be used to randomize teams. It's great for destroying clan stacks or for social events. It can be run by typing, by default, !randomize into in-game admin chat

+

Options

+ +
+
## Statement on Accuracy diff --git a/config.json b/config.json index 4f87a314..c2316e5a 100644 --- a/config.json +++ b/config.json @@ -33,16 +33,65 @@ }, "plugins": [ { - "plugin": "DiscordPlaceholder", + "plugin": "AutoKickUnassigned", + "enabled": true, + "warningMessage": "Join a squad, you are are unassigned and will be kicked", + "kickMessage": "Unassigned - automatically removed", + "frequencyOfWarnings": 30, + "unassignedTimer": 360, + "playerThreshold": 93, + "roundStartDelay": 900, + "ignoreAdmins": false, + "ignoreWhitelist": false + }, + { + "plugin": "AutoTKWarn", + "enabled": true, + "message": "Please apologise for ALL TKs in ALL chat!" + }, + { + "plugin": "ChatCommands", "enabled": true, + "commands": [ + { + "command": "squadjs", + "type": "warn", + "response": "This server is powered by SquadJS.", + "ignoreChats": [] + } + ] + }, + { + "plugin": "DBLog", + "enabled": false, + "database": "mysql", + "overrideServerID": null + }, + { + "plugin": "DiscordAdminBroadcast", + "enabled": false, "discordClient": "discord", - "command": "!placeholder" + "channelID": "", + "color": 16761867 }, { - "plugin": "DiscordSubsystemRestarter", + "plugin": "DiscordAdminCamLogs", "enabled": false, "discordClient": "discord", - "role": "" + "channelID": "", + "color": 16761867 + }, + { + "plugin": "DiscordAdminRequest", + "enabled": true, + "discordClient": "discord", + "channelID": "", + "ignoreChats": [], + "ignorePhrases": [], + "command": "admin", + "pingGroups": [], + "pingDelay": 60000, + "color": 16761867 }, { "plugin": "DiscordChat", @@ -56,22 +105,31 @@ ] }, { - "plugin": "DiscordAdminBroadcast", + "plugin": "DiscordDebug", "enabled": false, "discordClient": "discord", "channelID": "", - "color": 16761867 + "events": [] }, { - "plugin": "DiscordAdminRequest", + "plugin": "DiscordPlaceholder", + "enabled": true, + "discordClient": "discord", + "command": "!placeholder" + }, + { + "plugin": "DiscordRcon", + "enabled": false, + "discordClient": "discord", + "channelID": "", + "permissions": {}, + "prependAdminNameInBroadcast": false + }, + { + "plugin": "DiscordRoundWinner", "enabled": true, "discordClient": "discord", "channelID": "", - "ignoreChats": [], - "ignorePhrases": [], - "command": "admin", - "pingGroups": [], - "pingDelay": 60000, "color": 16761867 }, { @@ -83,23 +141,18 @@ "disableStatus": false }, { - "plugin": "AutoKickUnassigned", - "enabled": true, - "warningMessage": "Join a squad, you are are unassigned and will be kicked", - "kickMessage": "Unassigned - automatically removed", - "frequencyOfWarnings": 30, - "unassignedTimer": 360, - "playerThreshold": 93, - "roundStartDelay": 900, - "ignoreAdmins": false, - "ignoreWhitelist": false + "plugin": "DiscordSubsystemRestarter", + "enabled": false, + "discordClient": "discord", + "role": "" }, { - "plugin": "DiscordDebug", - "enabled": false, + "plugin": "DiscordTeamkill", + "enabled": true, "discordClient": "discord", "channelID": "", - "events": [] + "color": 16761867, + "disableSCBL": false }, { "plugin": "IntervalledBroadcasts", @@ -108,12 +161,11 @@ "interval": 300000 }, { - "plugin": "DiscordTeamkill", + "plugin": "SCBLInfo", "enabled": true, "discordClient": "discord", "channelID": "", - "color": 16761867, - "disableSCBL": false + "threshold": 6 }, { "plugin": "SeedingMode", @@ -126,61 +178,15 @@ "liveMessage": "Live!" }, { - "plugin": "AutoTKWarn", - "enabled": true, - "message": "Please apologise for ALL TKs in ALL chat!" - }, - { - "plugin": "DBLog", - "enabled": false, - "database": "mysql", - "overrideServerID": null - }, - { - "plugin": "DiscordRoundWinner", - "enabled": true, - "discordClient": "discord", - "channelID": "", - "color": 16761867 - }, - { - "plugin": "ChatCommands", - "enabled": true, - "commands": [ - { - "command": "squadjs", - "type": "warn", - "response": "This server is powered by SquadJS.", - "ignoreChats": [] - } - ] - }, - { - "plugin": "DiscordAdminCamLogs", + "plugin": "SquadIOAPI", "enabled": false, - "discordClient": "discord", - "channelID": "", - "color": 16761867 - }, - { - "plugin": "DiscordRcon", - "enabled": false, - "discordClient": "discord", - "channelID": "", - "permissions": {}, - "prependAdminNameInBroadcast": false + "websocketPort": "", + "securityToken": "" }, { "plugin": "TeamRandomizer", "enabled": true, "command": "randomize" - }, - { - "plugin": "SCBLInfo", - "enabled": true, - "discordClient": "discord", - "channelID": "", - "threshold": 6 } ], "logger": { diff --git a/squad-server/layers/layers.js b/squad-server/layers/layers.js index 5a3b3ef2..f01c5e2f 100644 --- a/squad-server/layers/layers.js +++ b/squad-server/layers/layers.js @@ -20,23 +20,10 @@ class Layers { this.layers = []; - Logger.verbose('Layers', 1, 'Pulling layers from GitHub...'); - let response = await axios - .get( - 'https://raw.githubusercontent.com/Squad-Wiki-Editorial/squad-wiki-pipeline-map-data/dev/completed_output/_Current%20Version/finished.json' - ) - .catch(() => { - Logger.verbose('Layers', 1, 'Failed to pull layers from GitHub moving to JSDelivr'); - }); - if (!response?.data) { - response = await axios - .get( - 'https://cdn.jsdelivr.net/gh/Squad-Wiki-Editorial/squad-wiki-pipeline-map-data@dev/completed_output/_Current%20Version/finished.json' - ) - .catch(() => { - throw new Error('Failed to pull layers from Github and JSDelivr!'); - }); - } + Logger.verbose('Layers', 1, 'Pulling layers...'); + const response = await axios.get( + 'https://raw.githubusercontent.com/Squad-Wiki-Editorial/squad-wiki-pipeline-map-data/dev/completed_output/_Current%20Version/finished.json' + ); for (const layer of response.data.Maps) { this.layers.push(new Layer(layer)); diff --git a/squad-server/package.json b/squad-server/package.json index b3922c88..8e63448c 100644 --- a/squad-server/package.json +++ b/squad-server/package.json @@ -15,6 +15,7 @@ "pg": "^8.5.1", "pg-hstore": "^2.3.3", "sequelize": "^6.3.5", + "socket.io": "^3.1.2", "sqlite3": "^5.0.0", "tedious": "^9.2.1", "tinygradient": "^1.1.2" diff --git a/squad-server/plugins/squad-io-api.js b/squad-server/plugins/squad-io-api.js new file mode 100644 index 00000000..9d690d3f --- /dev/null +++ b/squad-server/plugins/squad-io-api.js @@ -0,0 +1,133 @@ +import { createServer } from 'http'; +import { Server } from 'socket.io'; + +import BasePlugin from './base-plugin.js'; + +export default class SquadIOAPI extends BasePlugin { + static get description() { + return 'The SquadIOAPI plugin allows remote access to a SquadJS instance via Socket.IO'; + } + + static get defaultEnabled() { + return false; + } + + static get optionsSpecification() { + return { + websocketPort: { + required: true, + description: 'The port for the websocket.', + default: '', + example: '3000' + }, + securityToken: { + required: true, + description: 'Your secret token/password for connecting.', + default: '', + example: 'MySecretPassword' + } + }; + } + + constructor(server, options, connectors) { + super(server, options, connectors); + + this.httpServer = createServer(); + + this.io = new Server(this.httpServer, { + cors: { + origin: 'http://localhost:3000', + methods: ['GET', 'POST'], + allowedHeaders: ['squadJS-connection-panel'], + credentials: true + } + }); + + this.io.use((socket, next) => { + if (socket.handshake.auth && socket.handshake.auth.token === this.options.securityToken) { + next(); + } else { + next(new Error('Invalid token.')); + } + }); + + this.io.on('connection', (socket) => { + this.verbose(1, 'New Connection Made.'); + this.bindListeners(socket, this.server); + this.bindListeners(socket, this.server.rcon, 'rcon.'); + }); + } + + async mount() { + this.httpServer.listen(this.options.websocketPort); + } + + async unmount() { + this.httpServer.close(); + } + + bindListeners(socket, obj, prefix = '') { + const ignore = [ + 'options', + 'constructor', + 'watch', + 'unwatch', + 'setupRCON', + 'setupLogParser', + 'getPlayerByCondition', + 'pingSquadJSAPI', + '_events', + '_eventsCount', + '_maxListeners', + 'plugins', + 'rcon', + 'logParser', + 'updatePlayerListInterval', + 'updatePlayerListTimeout', + 'updateLayerInformationInterval', + 'updateLayerInformationTimeout', + 'updateA2SInformationInterval', + 'updateA2SInformationTimeout', + 'pingSquadJSAPIInterval', + 'pingSquadJSAPI', + 'pingSquadJSAPITimeout', + 'rcon.constructor', + 'rcon.processChatPacket', + 'rcon._events', + 'rcon._eventsCount', + 'rcon._maxListeners', + 'rcon.password', + 'rcon.connect', + 'rcon.onData', + 'rcon.onClose', + 'rcon.onError', + 'rcon.client', + 'rcon.autoReconnect', + 'rcon.autoReconnectTimeout', + 'rcon.incomingData', + 'rcon.incomingResponse', + 'rcon.responseCallbackQueue' + ]; + for (const key of Object.getOwnPropertyNames(Object.getPrototypeOf(obj))) { + if (ignore.includes(`${prefix}${key}`)) continue; + this.verbose(1, `Setting method listener for ${prefix}${key}...`); + socket.on(`${prefix}${key}`, async (...rawArgs) => { + const args = rawArgs.slice(0, rawArgs.length - 1); + const callback = rawArgs[rawArgs.length - 1]; + this.verbose(1, `Call to ${prefix}${key}(${args.join(', ')})`); + const reponse = await obj[key](...args); + callback(reponse); + }); + } + + for (const key of Object.getOwnPropertyNames(obj)) { + if (ignore.includes(`${prefix}${key}`)) continue; + this.verbose(1, `Setting properties listener for ${prefix}${key}...`); + socket.on(`${prefix}${key}`, (callback) => { + this.verbose(1, `Call to ${prefix}${key}...`); + const reponse = obj[key]; + callback(reponse); + }); + } + } +}