diff --git a/README.md b/README.md index a21144e9..e1b720e3 100644 --- a/README.md +++ b/README.md @@ -639,6 +639,29 @@ Grafana:
16761867
+
+ DiscordRoundEnded +

DiscordRoundEnded

+

The DiscordRoundEnded plugin will send the round winner to a Discord channel.

+

Options

+ +
+
DiscordServerStatus

DiscordServerStatus

diff --git a/config.json b/config.json index 37470001..e68f5e58 100644 --- a/config.json +++ b/config.json @@ -166,6 +166,13 @@ "channelID": "", "color": 16761867 }, + { + "plugin": "DiscordRoundEnded", + "enabled": true, + "discordClient": "discord", + "channelID": "", + "color": 16761867 + }, { "plugin": "DiscordServerStatus", "enabled": true, diff --git a/squad-server/index.js b/squad-server/index.js index 60233379..c366be2f 100644 --- a/squad-server/index.js +++ b/squad-server/index.js @@ -290,6 +290,10 @@ export default class SquadServer extends EventEmitter { this.emit('PLAYER_UNPOSSESS', data); }); + this.logParser.on('ROUND_ENDED', async (data) => { + this.emit('ROUND_ENDED', data); + }); + this.logParser.on('TICK_RATE', (data) => { this.emit('TICK_RATE', data); }); diff --git a/squad-server/log-parser/index.js b/squad-server/log-parser/index.js index 990f41fd..43588684 100644 --- a/squad-server/log-parser/index.js +++ b/squad-server/log-parser/index.js @@ -11,6 +11,8 @@ import PlayerPossess from './player-possess.js'; import PlayerRevived from './player-revived.js'; import PlayerUnPossess from './player-un-possess.js'; import PlayerWounded from './player-wounded.js'; +import RoundEnded from './round-ended.js'; +import RoundTickets from './round-tickets.js'; import RoundWinner from './round-winner.js'; import ServerTickRate from './server-tick-rate.js'; import SteamIDConnected from './steamid-connected.js'; @@ -34,6 +36,8 @@ export default class SquadLogParser extends LogParser { PlayerRevived, PlayerUnPossess, PlayerWounded, + RoundEnded, + RoundTickets, RoundWinner, ServerTickRate, SteamIDConnected, diff --git a/squad-server/log-parser/round-ended.js b/squad-server/log-parser/round-ended.js new file mode 100644 index 00000000..4b441992 --- /dev/null +++ b/squad-server/log-parser/round-ended.js @@ -0,0 +1,21 @@ +/** + * Matches when Map state Changes to PostMatch (ScoreBoard) + * + * Emits winner and loser from eventstore + * + * winner and loser may be null if the match ends with a draw + */ +export default { + regex: + /^\[([0-9.:-]+)]\[([ 0-9]*)]LogGameState: Match State Changed from InProgress to WaitingPostMatch/, + onMatch: (args, logParser) => { + const data = { + winner: logParser.eventStore.ROUND_WINNER ? logParser.eventStore.ROUND_WINNER : null, + loser: logParser.eventStore.ROUND_LOSER ? logParser.eventStore.ROUND_LOSER : null, + time: args[1] + }; + logParser.emit('ROUND_ENDED', data); + delete logParser.eventStore.ROUND_WINNER; + delete logParser.eventStore.ROUND_LOSER; + } +}; diff --git a/squad-server/log-parser/round-tickets.js b/squad-server/log-parser/round-tickets.js new file mode 100644 index 00000000..646241bd --- /dev/null +++ b/squad-server/log-parser/round-tickets.js @@ -0,0 +1,28 @@ +/** + * Matches when tickets appear in the log + * + * Will not match on Draw or Map Changes before the game has started + */ +export default { + regex: + /^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadGameEvents: Display: Team ([0-9]), (.*) \( ?(.*?) ?\) has (won|lost) the match with ([0-9]+) Tickets on layer (.*) \(level (.*)\)!/, + onMatch: (args, logParser) => { + const data = { + raw: args[0], + time: args[1], + chainID: args[2], + team: args[3], + subfaction: args[4], + faction: args[5], + action: args[6], + tickets: args[7], + layer: args[8], + level: args[9] + }; + if (data.action === 'won') { + logParser.eventStore.ROUND_WINNER = data; + } else { + logParser.eventStore.ROUND_LOSER = data; + } + } +}; diff --git a/squad-server/plugins/discord-roundended.js b/squad-server/plugins/discord-roundended.js new file mode 100644 index 00000000..f9757b16 --- /dev/null +++ b/squad-server/plugins/discord-roundended.js @@ -0,0 +1,79 @@ +import DiscordBasePlugin from './discord-base-plugin.js'; + +export default class DiscordRoundEnded extends DiscordBasePlugin { + static get description() { + return 'The DiscordRoundEnded plugin will send the round winner to a Discord channel.'; + } + + static get defaultEnabled() { + return true; + } + + static get optionsSpecification() { + return { + ...DiscordBasePlugin.optionsSpecification, + channelID: { + required: true, + description: 'The ID of the channel to log round end events to.', + default: '', + example: '667741905228136459' + }, + color: { + required: false, + description: 'The color of the embed.', + default: 16761867 + } + }; + } + + constructor(server, options, connectors) { + super(server, options, connectors); + + this.onRoundEnd = this.onRoundEnd.bind(this); + } + + async mount() { + this.server.on('ROUND_ENDED', this.onRoundEnd); + } + + async unmount() { + this.server.removeEventListener('ROUND_ENDED', this.onRoundEnd); + } + + async onRoundEnd(info) { + if (!info.winner || !info.loser) { + await this.sendDiscordMessage({ + embed: { + title: 'Round Ended', + description: 'This match Ended in a Draw', + color: this.options.color, + timestamp: info.time.toISOString() + } + }); + return; + } + + await this.sendDiscordMessage({ + embed: { + title: 'Round Ended', + description: `${info.winner.layer} - ${info.winner.level}`, + color: this.options.color, + fields: [ + { + name: `Team ${info.winner.team} Won`, + value: `${info.winner.subfaction}\n ${info.winner.faction}\n won with ${info.winner.tickets} tickets.` + }, + { + name: `Team ${info.loser.team} Lost`, + value: `${info.loser.subfaction}\n ${info.loser.faction}\n lost with ${info.loser.tickets} tickets.` + }, + { + name: 'Ticket Difference', + value: `${info.winner.tickets - info.loser.tickets}.` + } + ], + timestamp: info.time.toISOString() + } + }); + } +}