A modern, type-safe server management framework for Squad game servers.
Built with TypeScript and Bun for optimal performance, SquadScript provides robust RCON management, log parsing, and an extensible plugin system.
SquadScript is a scripting framework designed for Squad servers that handles all communication and data collection to and from your servers. It allows you to easily manage your server with powerful plugins without having to worry about the hassle of RCON or log parsing. SquadScript comes with multiple plugins already built for you, allowing you to experience its power right away.
SquadScript must be hosted on the same server box as your Squad server, or be connected to your Squad server via FTP/SFTP for log access.
- ๐ Type-Safe - Full TypeScript with compile-time type checking
- ๐ Fast - Built on Bun runtime for optimal performance
- ๐ Extensible Plugin System - Easy to create and share plugins
- ๐ก Multiple Log Readers - Support for local, FTP, and SFTP log access
- ๐ฎ Rich Event System - React to player events, kills, chat, and more
- ๐ค Discord Integration - Built-in Discord plugins for chat relay, admin alerts, and more
- ๐ Community Ban List (CBL) - Integration for checking player reputation
- โก Result-Based Error Handling - Explicit error handling without exceptions
- SquadScript
- Bun v1.3.0 or higher - Download
- Squad Server with RCON enabled
- Discord Bot Token (optional, for Discord plugins)
- Download SquadScript and unzip the download.
- Open the unzipped folder in your terminal.
- Install the dependencies:
bun install
- Build the packages:
bun run build
- Configure the
config.jsonfile. See Configuration for details. - Start SquadScript:
bun run start
Note: If you want to test the latest development version, clone the
mainbranch directly instead of downloading a release.
Edit the config.json file in the root directory:
{
"servers": [
{
"id": 1,
"name": "My Squad Server",
"rcon": {
"host": "127.0.0.1",
"port": 21114,
"password": "your-rcon-password"
},
"logReader": {
"mode": "tail",
"logDir": "C:/servers/squad_server/SquadGame/Saved/Logs"
},
"plugins": [
{
"plugin": "SeedingMode",
"enabled": true,
"options": {
"seedingThreshold": 50,
"seedingMessage": "Seeding Rules Active! Fight only over the middle flags!"
}
},
{
"plugin": "AutoTKWarn",
"enabled": true
}
]
}
]
}bun run startThat's it! SquadScript will connect to your server and start running the enabled plugins.
The config file needs to be valid JSON syntax. If an error is thrown saying the config cannot be parsed, try putting the config into a JSON syntax checker.
The main configuration file supports single or multi-server setups:
{
"servers": [
{
"id": 1,
"name": "Server Display Name",
"rcon": { /* RCON config */ },
"logReader": { /* Log reader config */ },
"adminLists": [ /* Admin list sources */ ],
"connectors": { /* Connector configs (Discord, etc.) */ },
"plugins": [ /* Plugin configurations */ ],
"verbosity": { /* Logging verbosity */ }
}
],
"connectors": { /* Global connectors shared across servers */ },
"verbosity": { /* Global verbosity settings */ }
}| Option | Description |
|---|---|
id |
An integer ID to uniquely identify the server |
name |
Human-readable server name |
rcon |
RCON connection configuration |
logReader |
Log reader configuration |
adminLists |
Sources for identifying admins on the server |
connectors |
Server-specific connector configurations |
plugins |
Array of plugin configurations |
verbosity |
Logging verbosity settings |
{
"rcon": {
"host": "127.0.0.1",
"port": 21114,
"password": "your-rcon-password",
"autoReconnect": true,
"autoReconnectDelay": 5000,
"maxReconnectAttempts": 0,
"timeout": 10000
}
}| Option | Type | Default | Description |
|---|---|---|---|
host |
string | - | The IP of the server |
port |
number | 21114 | The RCON port of the server |
password |
string | - | The RCON password of the server |
autoReconnect |
boolean | true |
Auto-reconnect on disconnect |
autoReconnectDelay |
number | 5000 |
Delay between reconnection attempts (ms) |
maxReconnectAttempts |
number | 0 |
Max reconnection attempts (0 = infinite) |
timeout |
number | 10000 |
Connection timeout (ms) |
SquadScript supports three modes for reading server logs:
Use tail mode when SquadScript is hosted on the same machine as your Squad server.
{
"logReader": {
"mode": "tail",
"logDir": "C:/servers/squad_server/SquadGame/Saved/Logs",
"filename": "SquadGame.log"
}
}Use ftp mode to read logs from a remote server via FTP.
{
"logReader": {
"mode": "ftp",
"logDir": "/remote/path/to/logs",
"ftp": {
"host": "ftp.example.com",
"port": 21,
"username": "your-username",
"password": "your-password",
"secure": false
}
}
}Use sftp mode for secure remote log access.
{
"logReader": {
"mode": "sftp",
"logDir": "/remote/path/to/logs",
"ftp": {
"host": "sftp.example.com",
"port": 22,
"username": "your-username",
"password": "your-password"
}
}
}Load admin permissions from multiple sources:
{
"adminLists": [
{
"type": "local",
"source": "C:/servers/squad_server/SquadGame/ServerConfig/Admins.cfg"
},
{
"type": "remote",
"source": "https://example.com/admins.txt",
"refreshInterval": 300000
},
{
"type": "ftp",
"source": "/remote/path/Admins.cfg",
"refreshInterval": 300000
}
]
}| Type | Description |
|---|---|
local |
Read from a local file path |
remote |
Fetch from a URL |
ftp |
Read from an FTP/SFTP server |
Connectors allow SquadScript to communicate with external resources. They should be named and configured, then referenced by plugins.
{
"connectors": {
"discord": {
"type": "discord",
"token": "your-discord-bot-token",
"guildId": "your-guild-id"
}
}
}Requires a Discord bot login token. Create a bot at the Discord Developer Portal.
Plugins are configured in the plugins array. Each plugin can be enabled/disabled and configured with options.
{
"plugins": [
{
"plugin": "PluginName",
"enabled": true,
"options": {
"option1": "value1",
"option2": 123
},
"connectors": {
"discordClient": "discord"
}
}
]
}| Field | Description |
|---|---|
plugin |
The name of the plugin |
enabled |
Toggle true/false to enable/disable the plugin |
options |
Plugin-specific configuration options |
connectors |
Map connector names to the plugin (for Discord plugins, etc.) |
Control console output verbosity for different modules:
{
"verbosity": {
"SquadServer": 1,
"RconClient": 1,
"LogParser": 1,
"PluginManager": 2
}
}Higher numbers produce more verbose output.
The following is a list of plugins built into SquadScript. Click to expand for configuration details.
ChatCommands - Listen for !command patterns in chat
Listen for !command patterns in chat and respond with preset messages.
{
"plugin": "ChatCommands",
"enabled": true,
"options": {
"commands": [
{ "command": "rules", "type": "warn", "response": "Please follow server rules!" },
{ "command": "discord", "type": "broadcast", "response": "Join our Discord: discord.gg/example" }
]
}
}| Option | Description | Default |
|---|---|---|
commands |
Array of command definitions | [] |
commands[].command |
Command trigger (without ! prefix) |
- |
commands[].type |
warn (to player) or broadcast (to all) |
- |
commands[].response |
Message to send | - |
AutoTKWarn - Automatically warn players when they teamkill
Sends warning messages to players when they teamkill.
{
"plugin": "AutoTKWarn",
"enabled": true,
"options": {
"attackerMessage": "Please apologise for ALL TKs in ALL chat!",
"victimMessage": "You were teamkilled. The player has been warned."
}
}| Option | Description | Default |
|---|---|---|
attackerMessage |
Message sent to the team killer | "Please apologise for ALL TKs in ALL chat!" |
victimMessage |
Message sent to the victim (null to disable) | null |
SeedingMode - Broadcast seeding rules when below player threshold
Broadcasts seeding rule messages when the server is below a player count threshold.
{
"plugin": "SeedingMode",
"enabled": true,
"options": {
"seedingThreshold": 50,
"seedingMessage": "Seeding Rules Active! Fight only over the middle flags!",
"liveEnabled": true,
"liveThreshold": 52,
"liveMessage": "Server is now LIVE! Normal rules apply.",
"interval": 150000,
"waitOnNewGames": true,
"waitTimeOnNewGame": 30000
}
}| Option | Description | Default |
|---|---|---|
seedingThreshold |
Player count below which seeding rules are active | 50 |
seedingMessage |
Message broadcast when seeding | "Seeding Rules Active!..." |
liveEnabled |
Enable "Live" announcements | true |
liveThreshold |
Player count for "Live" message | 52 |
liveMessage |
Message when server goes live | "Server is now LIVE! Normal rules apply." |
interval |
Broadcast interval (ms) | 150000 |
waitOnNewGames |
Pause broadcasts after new game starts | true |
waitTimeOnNewGame |
How long to pause (ms) | 30000 |
IntervalledBroadcasts - Broadcast rotating messages at intervals
Broadcasts messages from a list in rotation at regular intervals.
{
"plugin": "IntervalledBroadcasts",
"enabled": true,
"options": {
"broadcasts": [
"Welcome to our server! Follow the rules.",
"Join our Discord: discord.gg/example",
"Report issues to admins using !admin"
],
"interval": 300000
}
}| Option | Description | Default |
|---|---|---|
broadcasts |
Array of messages to broadcast | [] |
interval |
Interval between broadcasts (ms) | 300000 |
AutoKickUnassigned - Kick players who remain unassigned
Automatically kicks players who remain unassigned (not in a squad) for too long.
{
"plugin": "AutoKickUnassigned",
"enabled": true,
"options": {
"warningMessage": "Join a squad, you are unassigned and will be kicked",
"kickMessage": "Unassigned - automatically removed",
"frequencyOfWarnings": 30000,
"unassignedTimer": 360000,
"playerThreshold": 93,
"roundStartDelay": 900000,
"ignoreAdmins": false,
"ignoreWhitelist": false
}
}| Option | Description | Default |
|---|---|---|
warningMessage |
Warning message sent to unassigned players | "Join a squad..." |
kickMessage |
Kick reason shown to the player | "Unassigned - automatically removed" |
frequencyOfWarnings |
Interval between warnings (ms) | 30000 |
unassignedTimer |
Time before kick (ms) | 360000 |
playerThreshold |
Minimum player count for auto-kick (-1 to disable) | 93 |
roundStartDelay |
Grace period after round start (ms) | 900000 |
ignoreAdmins |
Exempt admins from auto-kick | false |
ignoreWhitelist |
Exempt reserved slot players | false |
FogOfWar - Set fog of war mode at round start
Automatically sets fog of war mode when a new game starts.
{
"plugin": "FogOfWar",
"enabled": true,
"options": {
"mode": 1,
"delay": 10000
}
}| Option | Description | Default |
|---|---|---|
mode |
Fog of war mode to set | 1 |
delay |
Delay after round start (ms) | 10000 |
TeamRandomizer - Randomize team assignments via admin command
Provides an admin command to randomize team assignments.
{
"plugin": "TeamRandomizer",
"enabled": true
}FirstBlood - Announce the first kill of each round
{
"plugin": "FirstBlood",
"enabled": true,
"options": {
"message": "{attacker} drew first blood against {victim}!"
}
}PlayerWelcome - Welcome players when they join
{
"plugin": "PlayerWelcome",
"enabled": true,
"options": {
"newPlayerMessage": "Welcome to the server, {player}!"
}
}RevengeTracker - Track and announce revenge kills
{
"plugin": "RevengeTracker",
"enabled": true
}RoundStatsSummary - Display round statistics at match end
{
"plugin": "RoundStatsSummary",
"enabled": true
}SquadNameEnforcer - Enforce squad naming conventions
{
"plugin": "SquadNameEnforcer",
"enabled": true,
"options": {
"blockedWords": ["offensive", "words"],
"warnMessage": "Please rename your squad appropriately"
}
}SquadLeaderChangeAlert - Alert when squad leaders change
{
"plugin": "SquadLeaderChangeAlert",
"enabled": true
}OrphanSquadLogger - Log squads that become leaderless
{
"plugin": "OrphanSquadLogger",
"enabled": true
}VehicleClaimTracker - Track vehicle claims by squads
{
"plugin": "VehicleClaimTracker",
"enabled": true
}Note: All Discord plugins require a Discord connector to be configured. See Connectors.
DiscordChat - Relay chat between in-game and Discord
{
"plugin": "DiscordChat",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"ignoreChats": ["ChatSquad"],
"color": 16766720
},
"connectors": { "discordClient": "discord" }
}| Option | Description | Default |
|---|---|---|
channelID |
Discord channel ID for chat relay | Required |
ignoreChats |
Chat channels to ignore | [] |
color |
Default embed color | 0xffd700 (gold) |
chatColors |
Colors for specific chat channels | {} |
DiscordTeamkill - Post teamkill alerts to Discord
{
"plugin": "DiscordTeamkill",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"color": 16711680,
"includeCBL": true
},
"connectors": { "discordClient": "discord" }
}DiscordAdminRequest - Forward admin requests to Discord
{
"plugin": "DiscordAdminRequest",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"command": "admin",
"pingGroups": ["987654321098765432"],
"pingHere": false,
"pingDelay": 60000,
"color": 16761867
},
"connectors": { "discordClient": "discord" }
}DiscordServerStatus - Post server status updates
{
"plugin": "DiscordServerStatus",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"updateInterval": 60000,
"showNextLayer": true
},
"connectors": { "discordClient": "discord" }
}DiscordAdminBroadcast - Relay admin broadcasts to Discord
{
"plugin": "DiscordAdminBroadcast",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"color": 16761867
},
"connectors": { "discordClient": "discord" }
}DiscordAdminCamLogs - Log admin camera usage
{
"plugin": "DiscordAdminCamLogs",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"color": 16761867
},
"connectors": { "discordClient": "discord" }
}DiscordKillFeed - Post kill feed to Discord
{
"plugin": "DiscordKillFeed",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"includeWounds": false,
"includeTeamkillsOnly": false
},
"connectors": { "discordClient": "discord" }
}DiscordRoundWinner - Announce round winners
{
"plugin": "DiscordRoundWinner",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"color": 16761867
},
"connectors": { "discordClient": "discord" }
}DiscordSquadCreated - Announce new squads
{
"plugin": "DiscordSquadCreated",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"color": 3329330
},
"connectors": { "discordClient": "discord" }
}CBLInfo - Check players against Community Ban List
Alerts admins when players with Community Ban List (CBL) reputation join.
{
"plugin": "CBLInfo",
"enabled": true,
"options": {
"channelID": "123456789012345678",
"threshold": 1,
"pingGroups": ["987654321098765432"],
"pingHere": false,
"color": 16729156
},
"connectors": { "discordClient": "discord" }
}| Option | Description | Default |
|---|---|---|
channelID |
Discord channel ID for CBL alerts | Required |
threshold |
Minimum reputation points to trigger alert (0 = any record) | 1 |
pingGroups |
Discord role IDs to ping on alerts | [] |
pingHere |
Use @here ping on alerts | false |
DBLog - Log events to a database
Logs server statistics and events to a database for analytics and stat tracking.
{
"plugin": "DBLog",
"enabled": true,
"options": {
"database": "mysql",
"serverID": 1
}
}SocketIOAPI - Real-time API via Socket.IO
Provides a real-time API for external applications.
{
"plugin": "SocketIOAPI",
"enabled": true,
"options": {
"port": 3000,
"authentication": "your-secret-token"
}
}| Option | Description | Default |
|---|---|---|
port |
Port for the Socket.IO server | 3000 |
authentication |
Security token for API access | Required |
Click to expand full config.json example
{
"servers": [
{
"id": 1,
"name": "My Community Squad Server",
"rcon": {
"host": "game.example.com",
"port": 21114,
"password": "secure-rcon-password"
},
"logReader": {
"mode": "sftp",
"logDir": "/home/squad/SquadGame/Saved/Logs",
"ftp": {
"host": "game.example.com",
"port": 22,
"username": "squad",
"password": "sftp-password"
}
},
"adminLists": [
{ "type": "local", "source": "/etc/squad/Admins.cfg" },
{ "type": "remote", "source": "https://api.example.com/admins.txt" }
],
"connectors": {
"discord": {
"type": "discord",
"token": "your-discord-bot-token"
}
},
"plugins": [
{
"plugin": "SeedingMode",
"enabled": true,
"options": {
"seedingThreshold": 40,
"seedingMessage": "๐ฑ Seeding Rules: Fight over middle objectives only!",
"liveThreshold": 50,
"liveMessage": "๐ฎ Server is LIVE! All rules apply!"
}
},
{
"plugin": "AutoTKWarn",
"enabled": true,
"options": {
"attackerMessage": "โ ๏ธ You teamkilled! Apologize in ALL chat immediately!"
}
},
{
"plugin": "AutoKickUnassigned",
"enabled": true,
"options": {
"playerThreshold": 80,
"unassignedTimer": 300000
}
},
{
"plugin": "IntervalledBroadcasts",
"enabled": true,
"options": {
"broadcasts": [
"๐ข Welcome! Server rules: No intentional TK, respect admins.",
"๐ฌ Join our Discord: discord.gg/example",
"๐ก๏ธ Need an admin? Use !admin in chat"
],
"interval": 600000
}
},
{
"plugin": "ChatCommands",
"enabled": true,
"options": {
"commands": [
{ "command": "rules", "type": "warn", "response": "1. No TK 2. Respect others 3. Follow admin instructions" },
{ "command": "discord", "type": "warn", "response": "Join us: discord.gg/example" }
]
}
},
{
"plugin": "DiscordChat",
"enabled": true,
"options": { "channelID": "111111111111111111" },
"connectors": { "discordClient": "discord" }
},
{
"plugin": "DiscordTeamkill",
"enabled": true,
"options": { "channelID": "222222222222222222" },
"connectors": { "discordClient": "discord" }
},
{
"plugin": "DiscordAdminRequest",
"enabled": true,
"options": {
"channelID": "333333333333333333",
"pingGroups": ["444444444444444444"]
},
"connectors": { "discordClient": "discord" }
},
{
"plugin": "CBLInfo",
"enabled": true,
"options": {
"channelID": "555555555555555555",
"threshold": 3,
"pingGroups": ["444444444444444444"]
},
"connectors": { "discordClient": "discord" }
}
]
}
]
}Interested in creating your own plugin? SquadScript makes it easy with a type-safe plugin API.
Click to expand custom plugin guide
Create custom plugins by extending the BasePlugin class:
import { BasePlugin } from '@squadscript/server';
import type { OptionsSpec, PluginMeta } from '@squadscript/types';
const optionsSpec = {
greeting: {
type: 'string',
required: false,
description: 'Greeting message for players',
default: 'Welcome to the server!',
},
} as const satisfies OptionsSpec;
export class MyCustomPlugin extends BasePlugin<typeof optionsSpec> {
static readonly meta: PluginMeta = {
name: 'MyCustomPlugin',
description: 'A custom plugin example',
version: '1.0.0',
defaultEnabled: true,
};
static readonly optionsSpec = optionsSpec;
async mount(): Promise<void> {
this.on('PLAYER_CONNECTED', async (event) => {
this.log.info(`Player connecting: ${event.player.eosID}`);
});
this.on('PLAYER_JOIN_SUCCEEDED', async (event) => {
await this.rcon.warn(event.player.eosID, this.options.greeting);
this.log.info(`Welcomed player: ${event.player.name}`);
});
}
}- Constructor - Plugin is instantiated with options
prepareToMount()- Validate options, setup connectorsmount()- Subscribe to events, start intervalsunmount()- Cleanup resources, clear intervals
// Logging
this.log.info('Information message');
this.log.warn('Warning message');
this.log.error('Error message', new Error('details'));
this.log.debug('Debug message');
this.log.verbose('Verbose message');
// RCON Commands
await this.rcon.warn(eosID, 'Message');
await this.rcon.kick(eosID, 'Reason');
await this.rcon.ban(steamID, '1d', 'Reason'); // (target, duration, reason)
await this.rcon.broadcast('Server-wide message');
await this.rcon.execute('RawCommand');
// Server State
const players = this.server.players;
const squads = this.server.squads;
const currentLayer = this.server.currentLayer;
// Event Subscriptions
this.on('EVENT_NAME', async (event) => { /* handler */ });
// Timers (auto-cleanup on unmount)
this.setTimeout(() => { /* code */ }, delay);
this.setInterval(() => { /* code */ }, interval);
// Connectors
const discord = this.getConnector<DiscordConnector>('discord');RCON Connection Issues
- Verify RCON is enabled in your server's
Server.cfg - Check firewall rules allow the RCON port (default: 21114)
- Verify credentials are correct in your config
- Check server is running before starting SquadScript
Log Reader Issues
Local tail mode:
- Ensure the log directory path is correct
- Verify file permissions allow reading
FTP/SFTP mode:
- Test connection with an FTP client first
- Check firewall allows outbound connections
- Verify credentials and paths
Plugin Not Loading
- Check plugin name is spelled correctly
- Verify
enabled: truein the config - Check for required options
- Review logs for error messages
Discord Bot Not Working
- Verify bot token is valid
- Ensure bot has proper permissions in the server
- Check channel IDs are correct (right-click channel โ Copy ID)
- Verify the bot is in the correct guild
Common Errors
| Error | Solution |
|---|---|
RCON connection refused |
Check host, port, and firewall |
Authentication failed |
Verify RCON password |
Log file not found |
Check log directory path |
Discord connector not found |
Configure Discord connector before Discord plugins |
Permission denied |
Check file permissions for log reader |
Config cannot be parsed |
Validate JSON syntax with a JSON checker |
Some information SquadScript collects from Squad servers was never intended or designed to be collected. As a result, it is impossible for any framework to collect the same information with 100% accuracy. SquadScript aims to get as close as possible to that figure, however, it acknowledges that this is not possible in some specific scenarios.
Known limitations:
- Real-time data - Server and player information is updated periodically (default: every 30 seconds). Some information may be slightly out of date.
- Restarts during active games - If SquadScript is started during an active game, some player state information may be incomplete.
- Duplicate player names - If multiple players have the same name, SquadScript may not be able to uniquely identify them in some log events.
SquadScript/
โโโ packages/
โ โโโ config/ # Configuration schemas and loaders
โ โโโ log-parser/ # Squad log file parser
โ โโโ logger/ # Logging utilities
โ โโโ rcon/ # RCON client implementation
โ โโโ types/ # Shared TypeScript types
โโโ projects/
โ โโโ core/ # Main SquadServer class and plugin system
โ โโโ plugins/ # Official plugin collection
โโโ config.json # Your server configuration (create this)
โโโ package.json # Workspace root
# Run tests
bun test
# Type check
bun run check-types
# Lint
bun run lint
# Build all packages
bun run buildMIT
Contributions are welcome! Please read our contributing guidelines before submitting a pull request.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Built with โค๏ธ for the Squad community