From 9976545276017c0a53d7132c9b393a02b0ac70a8 Mon Sep 17 00:00:00 2001
From: "Michael J. Barton"
Date: Mon, 19 Aug 2024 22:32:34 -0700
Subject: [PATCH] Added FTP source type to admin lists. (#379)
* Added FTP support to admin lists.
* Removed debug messages. Added info to readme template.
* Ran yarn build-all
* Ran yarn lint
---------
Co-authored-by: Marek
---
README.md | 604 +++++++++---------
config.json | 200 +++---
squad-server/factory.js | 16 +-
squad-server/package.json | 1 +
squad-server/plugins/discord-base-plugin.js | 4 +-
squad-server/plugins/discord-server-status.js | 39 +-
squad-server/templates/readme-template.md | 4 +
squad-server/utils/admin-lists.js | 25 +
squad-server/utils/writable-buffer.js | 22 +
9 files changed, 489 insertions(+), 426 deletions(-)
create mode 100644 squad-server/utils/writable-buffer.js
diff --git a/README.md b/README.md
index 4913f4d9..ce6873c3 100644
--- a/README.md
+++ b/README.md
@@ -84,6 +84,10 @@ The following section of the configuration contains information about your Squad
{
"type": "remote",
"source": "http://yourWebsite.com/Server1/Admins.cfg",
+ },
+ {
+ "type": "ftp",
+ "source": "ftp://:@:/",
}
]
},
@@ -207,15 +211,26 @@ 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)
- 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
+ DiscordAdminCamLogs
+ DiscordAdminCamLogs
+ The DiscordAdminCamLogs
plugin will log in game admin camera usage to a Discord channel.
Options
- 667741905228136459
+color
+ Description
+ The color of the embed.
+ Default
+ 16761867
@@ -242,9 +257,9 @@ Interested in creating your own plugin? [See more here](./squad-server/plugins/r
- DiscordFOBHABExplosionDamage
- DiscordFOBHABExplosionDamage
- The DiscordFOBHABExplosionDamage
plugin logs damage done to FOBs and HABs by explosions to help identify engineers blowing up friendly FOBs and HABs.
+ DiscordRcon
+ DiscordRcon
+ The DiscordRcon
plugin allows a specified Discord channel to be used as a RCON console to run RCON commands.
Options
667741905228136459
-color
+permissions
Description
- The color of the embeds.
+
Default
- 16761867
+ {}
Example
+ {
+ "123456789123456789": [
+ "AdminBroadcast",
+ "AdminForceTeamChange",
+ "AdminDemoteCommander"
+ ]
+}
+prependAdminNameInBroadcast
+ Description
+ Prepend admin names when making announcements.
+ Default
+ false
@@ -288,9 +315,9 @@ Interested in creating your own plugin? [See more here](./squad-server/plugins/r
- DiscordChat
- DiscordChat
- The DiscordChat
plugin will log in-game chat to a Discord channel.
+ DiscordKillFeed
+ DiscordKillFeed
+ The DiscordKillFeed
plugin logs all wounds and related information to a Discord channel for admins to review.
Options
667741905228136459
-chatColors
- Description
- The color of the embed for each chat.
- Default
- {}
Example
- {
- "ChatAll": 16761867
-}
color
Description
- The color of the embed.
+ The color of the embeds.
Default
16761867
-ignoreChats
+disableCBL
Description
- A list of chat names to ignore.
+ Disable Community Ban List information.
Default
- [
- "ChatSquad"
-]
+ false
+
+
+
+ DiscordPlaceholder
+ DiscordPlaceholder
+ The DiscordPlaceholder
plugin allows you to make your bot create placeholder messages that can be used when configuring other plugins.
+ 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:
+
- Grafana is a cool way of viewing server statistics stored in the database.
+- Install Grafana.
+- Add your database as a datasource named
SquadJS
.
+- Import the SquadJS Dashboard to get a preconfigured MySQL only Grafana dashboard.
+- Install any missing Grafana plugins.
+ Options
+
+
+
+
+ CBLInfo
+ CBLInfo
+ The CBLInfo
plugin alerts admins when a harmful player is detected joining their server based on data from the Community Ban List.
+ Options
+ 667741905228136459
+threshold
+ Description
+ Admins will be alerted when a player has this or more reputation points. For more information on reputation points, see the Community Ban List's FAQ
+ Default
+ 6
@@ -349,32 +435,77 @@ Interested in creating your own plugin? [See more here](./squad-server/plugins/r
- DiscordAdminCamLogs
- DiscordAdminCamLogs
- The DiscordAdminCamLogs
plugin will log in game admin camera usage to a Discord channel.
+ ChatCommands
+ ChatCommands
+ The ChatCommands
plugin can be configured to make chat commands that broadcast or warn the caller with present messages.
+ Options
+ commands
+ Description
+ An array of objects containing the following properties:
command
- The command that initiates the message.type
- Either warn
or broadcast
.response
- The message to respond with.ignoreChats
- A list of chats to ignore the commands in. Use this to limit it to admins.
+ Default
+ [
+ {
+ "command": "squadjs",
+ "type": "warn",
+ "response": "This server is powered by SquadJS.",
+ "ignoreChats": []
+ }
+]
+
+
+
+ DiscordServerStatus
+ DiscordServerStatus
+ The DiscordServerStatus
plugin can be used to get the server status in Discord.
Options
667741905228136459
-color
+ sqlite
+command
Description
- The color of the embed.
+ Command name to get message.
Default
- 16761867
+ !status
+disableSubscriptions
+ Description
+ Whether to allow messages to be subscribed to automatic updates.
+ Default
+ false
+updateInterval
+ Description
+ How frequently to update the time in Discord.
+ Default
+ 60000
+setBotStatus
+ Description
+ Whether to update the bot's status with server information.
+ Default
+ true
- DiscordRcon
- DiscordRcon
- The DiscordRcon
plugin allows a specified Discord channel to be used as a RCON console to run RCON commands.
+ 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
+
+
+
+
+ DiscordChat
+ DiscordChat
+ The DiscordChat
plugin will log in-game chat to a Discord channel.
Options
667741905228136459
-permissions
+chatColors
Description
-
+ The color of the embed for each chat.
Default
{}
Example
{
- "123456789123456789": [
- "AdminBroadcast",
- "AdminForceTeamChange",
- "AdminDemoteCommander"
- ]
+ "ChatAll": 16761867
}
-prependAdminNameInBroadcast
+color
Description
- Prepend admin names when making announcements.
+ The color of the embed.
Default
- false
+ 16761867
+ignoreChats
+ Description
+ A list of chat names to ignore.
+ Default
+ [
+ "ChatSquad"
+]
@@ -454,56 +588,53 @@ Interested in creating your own plugin? [See more here](./squad-server/plugins/r
- AutoKickUnassigned
- AutoKickUnassigned
- The AutoKickUnassigned
plugin will automatically kick players that are not in a squad after a specified ammount of time.
+ DiscordDebug
+ DiscordDebug
+ The DiscordDebug
plugin can be used to help debug SquadJS by dumping SquadJS events to a Discord channel.
Options
- [
+ "PLAYER_DIED"
+]
+
+
+
+ DiscordSubsystemRestarter
+ DiscordSubsystemRestarter
+ The DiscordSubSystemRestarter
plugin allows you to manually restart SquadJS subsystems in case an issues arises with them.
!squadjs restartsubsystem rcon
!squadjs restartsubsystem logparser
+ Options
+
+
Example
+ 667741905228136459
- DiscordDebug
- DiscordDebug
- The DiscordDebug
plugin can be used to help debug SquadJS by dumping SquadJS events to a Discord channel.
+ DiscordSquadCreated
+ DiscordSquadCreated
+ The SquadCreated
plugin will log Squad Creation events to a Discord channel.
Options
667741905228136459
-events (Required)
+color
Description
- A list of events to dump.
+ The color of the embed.
Default
- []
Example
- [
- "PLAYER_DIED"
-]
+ 16761867
+useEmbed
+ Description
+ Send message as Embed
+ Default
+ true
- CBLInfo
- CBLInfo
- The CBLInfo
plugin alerts admins when a harmful player is detected joining their server based on data from the Community Ban List.
+ FogOfWar
+ FogOfWar
+ The FogOfWar
plugin can be used to automate setting fog of war mode.
Options
- discordClient (Required)
+ 667741905228136459
-threshold
+ 10000
+
+
+
+ IntervalledBroadcasts
+ IntervalledBroadcasts
+ The IntervalledBroadcasts
plugin allows you to set broadcasts, which will be broadcasted at preset intervals
+ Options
+
+ []
Example
+ [
+ "This server is powered by SquadJS."
+]
+interval
+ Description
+ Frequency of the broadcasts in milliseconds.
+ Default
+ 300000
@@ -575,74 +722,67 @@ Interested in creating your own plugin? [See more here](./squad-server/plugins/r
- 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:
-
- Grafana is a cool way of viewing server statistics stored in the database.
-- Install Grafana.
-- Add your database as a datasource named
SquadJS
.
-- Import the SquadJS Dashboard to get a preconfigured MySQL only Grafana dashboard.
-- Install any missing Grafana plugins.
+ AutoTKWarn
+ AutoTKWarn
+ The AutoTkWarn
plugin will automatically warn players with a message when they teamkill.
Options
- database (Required)
+ attackerMessage
Description
- The Sequelize connector to log server information to.
+ The message to warn attacking players with.
Default
- mysql
-overrideServerID
+ Please apologise for ALL TKs in ALL chat!
+victimMessage
Description
- A overridden server ID.
+ The message that will be sent to the victim.
Default
null
- DiscordSquadCreated
- DiscordSquadCreated
- The SquadCreated
plugin will log Squad Creation events to a Discord channel.
+ AutoKickUnassigned
+ AutoKickUnassigned
+ The AutoKickUnassigned
plugin will automatically kick players that are not in a squad after a specified ammount of time.
Options
- discordClient (Required)
+ warningMessage
Description
- Discord connector name.
+ Message SquadJS will send to players warning them they will be kicked
Default
- discord
-channelID (Required)
+ Join a squad, you are unassigned and will be kicked
+kickMessage
Description
- The ID of the channel to log Squad Creation events to.
+ Message to send to players when they are kicked
Default
-
Example
-
667741905228136459
-color
+ Unassigned - automatically removed
+frequencyOfWarnings
Description
- The color of the embed.
+ How often in Seconds should we warn the player about being unassigned?
Default
- 16761867
-useEmbed
+ 30
+unassignedTimer
Description
- Send message as Embed
+ How long in Seconds to wait before a unassigned player is kicked
Default
- true
-
-
-
- ChatCommands
- ChatCommands
- The ChatCommands
plugin can be configured to make chat commands that broadcast or warn the caller with present messages.
- Options
- commands
+ 360
+playerThreshold
Description
- An array of objects containing the following properties:
command
- The command that initiates the message.type
- Either warn
or broadcast
.response
- The message to respond with.ignoreChats
- A list of chats to ignore the commands in. Use this to limit it to admins.
+ Player count required for AutoKick to start kicking players, set to -1 to disable
Default
- [
- {
- "command": "squadjs",
- "type": "warn",
- "response": "This server is powered by SquadJS.",
- "ignoreChats": []
- }
-]
+ 93
+roundStartDelay
+ Description
+ Time delay in Seconds from start of the round before AutoKick starts kicking again
+ Default
+ 900
+ignoreAdmins
+ Description
+ true
: Admins will NOT be kickedfalse
: Admins WILL be kicked
+ Default
+ false
+ignoreWhitelist
+ Description
+ true
: Reserve slot players will NOT be kickedfalse
: Reserve slot players WILL be kicked
+ Default
+ false
@@ -673,63 +813,6 @@ Grafana:
false
-
- IntervalledBroadcasts
- IntervalledBroadcasts
- The IntervalledBroadcasts
plugin allows you to set broadcasts, which will be broadcasted at preset intervals
- Options
- broadcasts
- Description
- Messages to broadcast.
- Default
- []
Example
-
[
- "This server is powered by SquadJS."
-]
-interval
- Description
- Frequency of the broadcasts in milliseconds.
- Default
- 300000
-
-
-
- DiscordServerStatus
- DiscordServerStatus
- The DiscordServerStatus
plugin can be used to get the server status in Discord.
- Options
- discordClient (Required)
- Description
- Discord connector name.
- Default
- discord
-messageStore (Required)
- Description
- Sequelize connector name.
- Default
- sqlite
-command
- Description
- Command name to get message.
- Default
- !status
-disableSubscriptions
- Description
- Whether to allow messages to be subscribed to automatic updates.
- Default
- false
-updateInterval
- Description
- How frequently to update the time in Discord.
- Default
- 60000
-setBotStatus
- Description
- Whether to update the bot's status with server information.
- Default
- true
-
-
DiscordAdminRequest
DiscordAdminRequest
@@ -803,9 +886,9 @@ Grafana:
- DiscordKillFeed
- DiscordKillFeed
- The DiscordKillFeed
plugin logs all wounds and related information to a Discord channel for admins to review.
+ DiscordFOBHABExplosionDamage
+ DiscordFOBHABExplosionDamage
+ The DiscordFOBHABExplosionDamage
plugin logs damage done to FOBs and HABs by explosions to help identify engineers blowing up friendly FOBs and HABs.
Options
667741905228136459
@@ -822,86 +905,7 @@ Grafana:
Description
The color of the embeds.
Default
- 16761867
-disableCBL
- Description
- Disable Community Ban List information.
- Default
- false
-
-
-
- AutoTKWarn
- AutoTKWarn
- The AutoTkWarn
plugin will automatically warn players with a message when they teamkill.
- Options
- attackerMessage
- Description
- The message to warn attacking players with.
- Default
- Please apologise for ALL TKs in ALL chat!
-victimMessage
- Description
- The message that will be sent to the victim.
- Default
- null
-
-
-
- DiscordPlaceholder
- DiscordPlaceholder
- The DiscordPlaceholder
plugin allows you to make your bot create placeholder messages that can be used when configuring other plugins.
- Options
-
-
-
-
- FogOfWar
- FogOfWar
- The FogOfWar
plugin can be used to automate setting fog of war mode.
- Options
-
-
-
-
- DiscordSubsystemRestarter
- DiscordSubsystemRestarter
- The DiscordSubSystemRestarter
plugin allows you to manually restart SquadJS subsystems in case an issues arises with them.
!squadjs restartsubsystem rcon
!squadjs restartsubsystem logparser
- Options
- 667741905228136459
+ 16761867
diff --git a/config.json b/config.json
index 9928ba52..dbc65944 100644
--- a/config.json
+++ b/config.json
@@ -46,9 +46,11 @@
},
"plugins": [
{
- "plugin": "TeamRandomizer",
- "enabled": true,
- "command": "randomize"
+ "plugin": "DiscordAdminCamLogs",
+ "enabled": false,
+ "discordClient": "discord",
+ "channelID": "",
+ "color": 16761867
},
{
"plugin": "DiscordAdminBroadcast",
@@ -58,11 +60,12 @@
"color": 16761867
},
{
- "plugin": "DiscordFOBHABExplosionDamage",
- "enabled": true,
+ "plugin": "DiscordRcon",
+ "enabled": false,
"discordClient": "discord",
"channelID": "",
- "color": 16761867
+ "permissions": {},
+ "prependAdminNameInBroadcast": false
},
{
"plugin": "DiscordRoundWinner",
@@ -72,37 +75,77 @@
"color": 16761867
},
{
- "plugin": "DiscordChat",
- "enabled": true,
+ "plugin": "DiscordKillFeed",
+ "enabled": false,
"discordClient": "discord",
"channelID": "",
- "chatColors": {},
"color": 16761867,
- "ignoreChats": [
- "ChatSquad"
- ]
+ "disableCBL": false
},
{
- "plugin": "DiscordRoundEnded",
+ "plugin": "DiscordPlaceholder",
"enabled": true,
"discordClient": "discord",
- "channelID": "",
- "color": 16761867
+ "command": "!placeholder",
+ "channelID": ""
},
{
- "plugin": "DiscordAdminCamLogs",
+ "plugin": "DBLog",
"enabled": false,
+ "database": "mysql",
+ "overrideServerID": null
+ },
+ {
+ "plugin": "CBLInfo",
+ "enabled": true,
+ "discordClient": "discord",
+ "channelID": "",
+ "threshold": 6
+ },
+ {
+ "plugin": "DiscordRoundEnded",
+ "enabled": true,
"discordClient": "discord",
"channelID": "",
"color": 16761867
},
{
- "plugin": "DiscordRcon",
- "enabled": false,
+ "plugin": "ChatCommands",
+ "enabled": true,
+ "commands": [
+ {
+ "command": "squadjs",
+ "type": "warn",
+ "response": "This server is powered by SquadJS.",
+ "ignoreChats": []
+ }
+ ]
+ },
+ {
+ "plugin": "DiscordServerStatus",
+ "enabled": true,
+ "discordClient": "discord",
+ "messageStore": "sqlite",
+ "command": "!status",
+ "disableSubscriptions": false,
+ "updateInterval": 60000,
+ "setBotStatus": true
+ },
+ {
+ "plugin": "TeamRandomizer",
+ "enabled": true,
+ "command": "randomize"
+ },
+ {
+ "plugin": "DiscordChat",
+ "enabled": true,
"discordClient": "discord",
"channelID": "",
- "permissions": {},
- "prependAdminNameInBroadcast": false
+ "chatColors": {},
+ "color": 16761867,
+ "ignoreChats": [
+ "ChatSquad"
+ ]
},
{
"plugin": "SeedingMode",
@@ -116,18 +159,6 @@
"waitOnNewGames": true,
"waitTimeOnNewGame": 30
},
- {
- "plugin": "AutoKickUnassigned",
- "enabled": true,
- "warningMessage": "Join a squad, you are unassigned and will be kicked",
- "kickMessage": "Unassigned - automatically removed",
- "frequencyOfWarnings": 30,
- "unassignedTimer": 360,
- "playerThreshold": 93,
- "roundStartDelay": 900,
- "ignoreAdmins": false,
- "ignoreWhitelist": false
- },
{
"plugin": "DiscordDebug",
"enabled": false,
@@ -136,43 +167,54 @@
"events": []
},
{
- "plugin": "CBLInfo",
- "enabled": true,
+ "plugin": "DiscordSubsystemRestarter",
+ "enabled": false,
+ "discordClient": "discord",
+ "role": ""
+ },
+ {
+ "plugin": "DiscordSquadCreated",
+ "enabled": false,
"discordClient": "discord",
"channelID": "",
- "threshold": 6
+ "color": 16761867,
+ "useEmbed": true
},
{
- "plugin": "SocketIOAPI",
+ "plugin": "FogOfWar",
"enabled": false,
- "websocketPort": "",
- "securityToken": ""
+ "mode": 1,
+ "delay": 10000
},
{
- "plugin": "DBLog",
+ "plugin": "IntervalledBroadcasts",
"enabled": false,
- "database": "mysql",
- "overrideServerID": null
+ "broadcasts": [],
+ "interval": 300000
},
{
- "plugin": "DiscordSquadCreated",
+ "plugin": "SocketIOAPI",
"enabled": false,
- "discordClient": "discord",
- "channelID": "",
- "color": 16761867,
- "useEmbed": true
+ "websocketPort": "",
+ "securityToken": ""
},
{
- "plugin": "ChatCommands",
+ "plugin": "AutoTKWarn",
"enabled": true,
- "commands": [
- {
- "command": "squadjs",
- "type": "warn",
- "response": "This server is powered by SquadJS.",
- "ignoreChats": []
- }
- ]
+ "attackerMessage": "Please apologise for ALL TKs in ALL chat!",
+ "victimMessage": null
+ },
+ {
+ "plugin": "AutoKickUnassigned",
+ "enabled": true,
+ "warningMessage": "Join a squad, you are unassigned and will be kicked",
+ "kickMessage": "Unassigned - automatically removed",
+ "frequencyOfWarnings": 30,
+ "unassignedTimer": 360,
+ "playerThreshold": 93,
+ "roundStartDelay": 900,
+ "ignoreAdmins": false,
+ "ignoreWhitelist": false
},
{
"plugin": "DiscordTeamkill",
@@ -182,22 +224,6 @@
"color": 16761867,
"disableCBL": false
},
- {
- "plugin": "IntervalledBroadcasts",
- "enabled": false,
- "broadcasts": [],
- "interval": 300000
- },
- {
- "plugin": "DiscordServerStatus",
- "enabled": true,
- "discordClient": "discord",
- "messageStore": "sqlite",
- "command": "!status",
- "disableSubscriptions": false,
- "updateInterval": 60000,
- "setBotStatus": true
- },
{
"plugin": "DiscordAdminRequest",
"enabled": true,
@@ -214,37 +240,11 @@
"showInGameAdmins": true
},
{
- "plugin": "DiscordKillFeed",
- "enabled": false,
- "discordClient": "discord",
- "channelID": "",
- "color": 16761867,
- "disableCBL": false
- },
- {
- "plugin": "AutoTKWarn",
- "enabled": true,
- "attackerMessage": "Please apologise for ALL TKs in ALL chat!",
- "victimMessage": null
- },
- {
- "plugin": "DiscordPlaceholder",
+ "plugin": "DiscordFOBHABExplosionDamage",
"enabled": true,
"discordClient": "discord",
- "command": "!placeholder",
- "channelID": ""
- },
- {
- "plugin": "FogOfWar",
- "enabled": false,
- "mode": 1,
- "delay": 10000
- },
- {
- "plugin": "DiscordSubsystemRestarter",
- "enabled": false,
- "discordClient": "discord",
- "role": ""
+ "channelID": "",
+ "color": 16761867
}
],
"logger": {
diff --git a/squad-server/factory.js b/squad-server/factory.js
index 7bda552e..93f03831 100644
--- a/squad-server/factory.js
+++ b/squad-server/factory.js
@@ -2,7 +2,7 @@ import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
-import {Client, Events, GatewayIntentBits} from 'discord.js';
+import { Client, Events, GatewayIntentBits } from 'discord.js';
import sequelize from 'sequelize';
import AwnAPI from './utils/awn-api.js';
@@ -103,16 +103,20 @@ export default class SquadServerFactory {
Logger.verbose('SquadServerFactory', 1, `Starting ${type} connector ${connectorName}...`);
if (type === 'discord') {
- const connector = new Client({intents:[
+ const connector = new Client({
+ intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
- GatewayIntentBits.GuildMembers,
- ]});
- connector.once(Events.ClientReady, readyClient => {console.log(`Ready! Logged in as ${readyClient.user.tag}`);});
+ GatewayIntentBits.GuildMembers
+ ]
+ });
+ connector.once(Events.ClientReady, (readyClient) => {
+ console.log(`Ready! Logged in as ${readyClient.user.tag}`);
+ });
await connector.login(connectorConfig);
// setup compatability with older plugins for message create event.
- connector.on('messageCreate', message=>{
+ connector.on('messageCreate', (message) => {
connector.emit('message', message);
});
return connector;
diff --git a/squad-server/package.json b/squad-server/package.json
index cbe06f00..035bd4d7 100644
--- a/squad-server/package.json
+++ b/squad-server/package.json
@@ -4,6 +4,7 @@
"type": "module",
"dependencies": {
"axios": "^0.21.1",
+ "basic-ftp": "^4.6.6",
"core": "1.0.0",
"didyoumean": "^1.2.1",
"discord.js": "^14.14.1",
diff --git a/squad-server/plugins/discord-base-plugin.js b/squad-server/plugins/discord-base-plugin.js
index 1f1fffd0..5b339826 100644
--- a/squad-server/plugins/discord-base-plugin.js
+++ b/squad-server/plugins/discord-base-plugin.js
@@ -36,8 +36,8 @@ export default class DiscordBasePlugin extends BasePlugin {
if (typeof message === 'object' && 'embed' in message) {
message.embed.footer = message.embed.footer || { text: COPYRIGHT_MESSAGE };
if (typeof message.embed.color === 'string')
- message.embed.color = parseInt(message.embed.color,16);
- message = {...message, embeds:[message.embed]};
+ message.embed.color = parseInt(message.embed.color, 16);
+ message = { ...message, embeds: [message.embed] };
}
await this.channel.send(message);
diff --git a/squad-server/plugins/discord-server-status.js b/squad-server/plugins/discord-server-status.js
index e7d610c1..c187fced 100644
--- a/squad-server/plugins/discord-server-status.js
+++ b/squad-server/plugins/discord-server-status.js
@@ -54,7 +54,6 @@ export default class DiscordServerStatus extends DiscordBaseMessageUpdater {
}
async generateMessage() {
-
// Set player embed field.
let players = '';
@@ -65,24 +64,25 @@ export default class DiscordServerStatus extends DiscordBaseMessageUpdater {
players += ` / ${this.server.publicSlots}`;
if (this.server.reserveSlots > 0) players += ` (+${this.server.reserveSlots})`;
- const layerName = this.server.currentLayer ? this.server.currentLayer.name : (await this.server.rcon.getCurrentMap()).layer;
+ const layerName = this.server.currentLayer
+ ? this.server.currentLayer.name
+ : (await this.server.rcon.getCurrentMap()).layer;
// Clamp the ratio between 0 and 1 to avoid tinygradient errors.
const ratio = this.server.a2sPlayerCount / (this.server.publicSlots + this.server.reserveSlots);
const clampedRatio = Math.min(1, Math.max(0, ratio));
// Set gradient embed color.
- const color =
- parseInt(
- tinygradient([
- { color: '#ff0000', pos: 0 },
- { color: '#ffff00', pos: 0.5 },
- { color: '#00ff00', pos: 1 }
- ])
- .rgbAt(clampedRatio)
- .toHex(),
- 16
- );
+ const color = parseInt(
+ tinygradient([
+ { color: '#ff0000', pos: 0 },
+ { color: '#ffff00', pos: 0.5 },
+ { color: '#00ff00', pos: 1 }
+ ])
+ .rgbAt(clampedRatio)
+ .toHex(),
+ 16
+ );
const embedobj = {
title: this.server.serverName,
@@ -99,18 +99,21 @@ export default class DiscordServerStatus extends DiscordBaseMessageUpdater {
{
name: 'Next Layer',
value: `\`\`\`${
- this.server.nextLayer?.name || (this.server.nextLayerToBeVoted ? 'To be voted' : 'Unknown')
+ this.server.nextLayer?.name ||
+ (this.server.nextLayerToBeVoted ? 'To be voted' : 'Unknown')
}\`\`\``,
inline: true
}
],
color: color,
- footer: {text:COPYRIGHT_MESSAGE},
+ footer: { text: COPYRIGHT_MESSAGE },
timestamp: new Date(),
image: {
- url: (this.server.currentLayer ? `https://squad-data.nyc3.cdn.digitaloceanspaces.com/main/${this.server.currentLayer.layerid}.jpg` : undefined)
- },
- }
+ url: this.server.currentLayer
+ ? `https://squad-data.nyc3.cdn.digitaloceanspaces.com/main/${this.server.currentLayer.layerid}.jpg`
+ : undefined
+ }
+ };
return { embeds: [embedobj] };
}
diff --git a/squad-server/templates/readme-template.md b/squad-server/templates/readme-template.md
index 381aa644..940a9d48 100644
--- a/squad-server/templates/readme-template.md
+++ b/squad-server/templates/readme-template.md
@@ -84,6 +84,10 @@ The following section of the configuration contains information about your Squad
{
"type": "remote",
"source": "http://yourWebsite.com/Server1/Admins.cfg",
+ },
+ {
+ "type": "ftp",
+ "source": "ftp://:@:/",
}
]
},
diff --git a/squad-server/utils/admin-lists.js b/squad-server/utils/admin-lists.js
index 1bebb9a1..9ece8266 100644
--- a/squad-server/utils/admin-lists.js
+++ b/squad-server/utils/admin-lists.js
@@ -1,6 +1,8 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
+import { Client as FTPClient } from 'basic-ftp';
+import WritableBuffer from './writable-buffer.js';
import axios from 'axios';
import Logger from 'core/logger';
@@ -31,6 +33,29 @@ export default async function fetchAdminLists(adminLists) {
data = fs.readFileSync(listPath, 'utf8');
break;
}
+ case 'ftp': {
+ // ex url: ftp//:@:/
+ if (!list.source.startsWith('ftp://')) {
+ throw new Error(
+ `Invalid FTP URI format of ${list.source}. The source must be a FTP URI starting with the protocol. Ex: ftp://username:password@host:21/some/file.txt`
+ );
+ }
+ const [loginString, hostPathString] = list.source.substring('ftp://'.length).split('@');
+ const [user, password] = loginString.split(':').map((v) => decodeURI(v));
+ const pathStartIndex = hostPathString.indexOf('/');
+ const remoteFilePath =
+ pathStartIndex === -1 ? '/' : hostPathString.substring(pathStartIndex);
+ const [host, port = 21] = hostPathString
+ .substring(0, pathStartIndex === -1 ? hostPathString.length : pathStartIndex)
+ .split(':');
+
+ const buffer = new WritableBuffer();
+ const ftpClient = new FTPClient();
+ await ftpClient.access({ host, port, user, password });
+ await ftpClient.downloadTo(buffer, remoteFilePath);
+ data = buffer.toString('utf8');
+ break;
+ }
default:
throw new Error(`Unsupported AdminList type:${list.type}`);
}
diff --git a/squad-server/utils/writable-buffer.js b/squad-server/utils/writable-buffer.js
new file mode 100644
index 00000000..8df6ccf9
--- /dev/null
+++ b/squad-server/utils/writable-buffer.js
@@ -0,0 +1,22 @@
+import { Writable } from 'stream';
+
+export class WritableBuffer extends Writable {
+ constructor(options) {
+ super(options);
+ this.data = [];
+ }
+
+ _write(chunk, encoding, callback) {
+ this.data.push(chunk);
+ callback();
+ }
+
+ getBuffer() {
+ return Buffer.concat(this.data);
+ }
+
+ toString(encoding = 'utf8') {
+ return this.getBuffer().toString(encoding);
+ }
+}
+export default WritableBuffer;