/cftools-relay

An easy to use relay for cftools webhook events piped to Discord when filter rules match.

Primary LanguageGoMIT LicenseMIT

CFTools Relay

Discord Tests

CFTools Relay is an easy-to-use, still in development, tool that allows you to subscribe to CFTools Cloud Webhook events and forward them to a different target. Right now, the only target that is supported is as Discord Webhook URL.

Why?

CFTools Relay is a tool that is mostly done to allow filtering for specific CFTools events. CFTools Cloud already provides a way to send Webhooks for specific events to Discord, however, apart from the event name, there is no additional filter criteria that can be applied.

If one, e.g., would like to get a notification in Discord only, if a player kills another player from more than 500 meters away, potentially with a specific weapon (like an IJ 70) only, this is currently not possible with CFTools Webhooks. CFTools Relay allows to define such filters, which are applied to the incoming Webhook events. Only if at least one filter, with all filter rules, match the event, it will be forwarded/relayed to Discord.

Should I use this tool?

If you do not need to filter Webhook events based on data that is within an event (rather than just the event type), then you SHOULD NOT use this tool. It is only increasing complexity in this case, and you can achieve the very same outcome with the CFTools Cloud builtin Webhook-to-Discord feature. Simply use that.

If you, however, need some more filter logic on top of your Webhook events to further reduce the amount of messages your channel is flooded with, this tool might be of help.

Installation

The installation is as simple as downloading the latest version of this tool, put it somewhere and start it, either by double-clicking on the exe file (for Windows OS) or running the tool in a terminal (for Linux and Windows OS).

Download the latest version

You can either visit the release page to download the latest released version (which might miss some features from the current development version).

Alternatively, you can visit the automated build page, select the most recent successful run (with a green checkmark) and download the artifact for your operating system from the Artifacts section.

Configuration

Upon the first start of the tool, it will automatically create a configuration file, named config.json in the same directory as the binary.

Check port of the tool and your firewall

This tool will start an internal webserver, which is used to handle webhook events sent from CFTools to it. The webserver, by default, uses port 8080 to listen on. You need to ensure that this port is available to the tool and not used by another program/process already (like another webserver). If you do not want to use port 8080 for this tool, you can change the port in the created configuration file.

Also, make sure that the port you configured (or the default one) is whitelisted in your firewall configuration. The CFTools Relay is using an unencrypted connection using http. If you want to use a TLS-encrypted connection for your webhook messages, you may want to setup a reverse proxy for this tool, which handles the TLS termination. A setup like that is out-of-scope of this README.

First time setup

When you setup this tool the first time, you need to do some basic steps in order to create and verify the webhook in the CFTools Cloud console:

  1. Start the tool (if you did not do that already) and ensure it is able to receive web requests (see Check port of the tool and your firewall)
  2. Open the config.json of CFTools Relay in your favourite text editor
  3. Create a new server entry in the servers object of the config and choose a name for the server (you can not use whitespaces or other characters that are not URL-safe). It should look like this (when your server name is aServerName):
    • "servers": {"aServerName": {"Secret": ""}},
  4. Go to the CFTools Cloud Dashboard and open the server where the webhook should be added to
  5. Navigate to the Manage -> Integrations page of that server
  6. Add a new Webhook with the New Webhook button
  7. Enter the Webhook URL:
    • It consists of the public IP address or domain of your server (e.g. http://123.123.123.123)
    • appended is the Port (by default 8080) separated by a colon (:8080)
    • at the end it needs to have a fixed path together with the server name: /cftools-webhook/aServerName
    • For the example values above, the full webhook URL looks like: http://123.123.123.123:8080/cftools-webhook/aServerName
  8. Select CFTools CLoud (Hephaistos v1) as the Payload format
  9. Click Deploy
  10. Reload the page and make sure it shows a green shield (which means Verified and active) next to the newly created webhook
  11. Open the Webhook details and copy the value in the Secret field
  12. Go back to the config.json of CFTools Relay in your text editor
  13. Paste the copied secret into the value of the secret field of your previously created server. It should then look like this (when your secret is abc123):
    • "servers": {"aServerName": "abc123"},
  14. Save the config.json file and restart the CFTools Relay tool
  15. Go back to the Webhook details page of CFTools Cloud and select every event you want the CFTools Relay to receive
  16. Hit Save

You're done with the configuration on the CFTools side. Given CFTools Relay does not know where to relay the webhook event messages to right now, you need to configure the Discord target:

  1. Go to the Discord channel of your choice where the Webhook messages should be relayed to
  2. Open the settings of this channel
  3. Navigate to the Integrations section of the settings
  4. Create a new webhook (or use an existing one, up to you)
  5. Copy the Webhook URL value
  6. Open the config.json of CFTools Relay in your favourite text editor 7Paste the copied URL into the value of the discord.webhook_url field inside the config. It should then look like this (when your URL is http://example.com):
"discord": {
  "webhook_url": "http://example.com"
}
  1. Save the config.json file and restart the CFTools Relay tool

That's it. The first time configuration is now done and CFTools Relay will now start to forward Webhook events from CFTools Cloud to your discord channel.

Multiple server setup

CFTools-relay supports multiple server out-of-the box. You can either run multiple instances of the tool (when you do not want to share the data of all servers in a shared data basis), or you can use one instance of CFTools-relay to serve webhooks of multiple CFTools servers.

In order to serve multiple webhook events from multiple servers in a single CFTools-relay instance, you need to configure one server object within the servers object of the config and set the secret as explained in the previous chapter. An example of how such a setup looks like:

  "servers": {
    "firstServer": {
      "secret": "the-secret",
      "name": "A custom ServerName"
    },
    "secondServer": {
      "secret": "the-secret"
    }
  },

Filter configuration

The main use case of CFTools Relay is to be able to filter events that should be forwarded to Discord. By default, there are no filters set up, which makes CFTools Relay to relay all messages to Discord.

There are two main concepts to understand before using filters:

Filters: You can have 0-n filters configured in CFTools Relay. Filters are evaluated independently of each other. CFTools-Relay will go through each filter one-by-one to match the event against. Each matching filter will be processed, which means that the filters are combined with an OR operator. Because of that, if you define multiple filters which would match a single event, the event may be relayed multiple times as well. The only required field for filters is the event name this filter should apply to.

Rules: Each filter can have 0-n rules that are evaluated to the webhook event. Only if all defined rules match the event, the filter evaluates to "match the event", which would make the event be relayed to Discord. Rules are therefore combined with an AND operator.

Rules allow you to define more in-depth filtering on Webhook events, as they allow you to compare specific fields to values you define. The available fields heavily depend on the Webhook event type (see the JSON files in the payloads/ folder of this project for some information).

A usual configuration with one example filter looks like:

{
  "port": 8080,
  "secret": "...",
  "discord": {
    "webhook_url": "..."
  },
  "filter": [
    {
      "event": "user.join",
      "rules": null
    },
    {
      "event": "user.leave",
      "rules": null
    }
  ]
}

This filter will make CFTools Relay to only relay events with the type user.join and user.leave.

Example 1: Relay kill event when distance is greater than 1000 meter

If you wish to get a kill notification in your Discord channel only, if the distance between the Victim and the Murderer is greater than 1000 meters, you can add the following filter:

{
  "event": "player.kill",
  "rules": [
    {
      "comparator": "gt",
      "field": "distance",
      "value": 1000
    }
  ]
}

Example 2: Relay kill event when distance is greater than 100 meter with an IJ-70

Based on the previous example, you can also combine multiple rules and make filters that, e.g., relay a message to Discord only, if the kill was made over 100 meters between the Victim and the Murderer, as well as if the murderer used an IJ-70:

{
  "event": "player.kill",
  "rules": [
    {
      "comparator": "gt",
      "field": "distance",
      "value": 100
    },
    {
      "comparator": "eq",
      "field": "weapon",
      "value": "IJ-70"
    }
  ]
}

Example 3: Relay kill message when done with one of multiple weapons and over a specific distance

This example filter configuration will relay a player.kill message only, if the murderer used an AKM or an M16-A2 and made the kill over 1000 meter away.

{
  "event": "player.kill",
  "rules": [
     {
        "comparator": "gt",
        "field": "distance",
        "value": 100
     },
     {
        "comparator": "oneOf",
        "field": "weapon",
        "value": ["KA-M", "M16-A2"]
     }
  ]
}

Available Comparators

The following table lists the available Comparators for filter rules:

Comparator Explanation
eq Equals comparator, matches only, if the value of the configured field in the event is exactly the configured value. This comparator is case-sensitive.
gt Greater than or equals comparator, matches only, if the value of the configured field in the event is a numeric value and is greater than or equals the configured value. This comparator never matches when the value is not a numeric field.
lt Less than or equals comparator, matches only, if the value of the configured field in the event is a numeric value and is les than or equals the configured value. This comparator never matches when the value is not a numeric field.
contains Contains comparator, matches only, if the value of the configured field in the event is a contains the configured value. This is a wildcard matcher, which is equivalent to *value* is wildcards would exist.
startsWith Starts with comparator, same as contains with the only difference, that the field value needs to start with the configured value (value* instead of *value*).
endsWith Ends with comparator, same as contains with the only difference, that the field value needs to end with the configured value (*value instead of *value*).
oneOf One of comparator, matches when the value of the configured field in the event is included in the list of values configured in value. The value needs to be an array of strings. If a string is given only, this comparator behaves like eq.

Virtual Fields

In addition to the fields received via the webhook from CFTools, CFTools Relay may inject available data for the specific event in it's own fields. These fields are also called virtual fields as they are only present and usable within CFTools Relay rule configuration.

Virtual Fields aim to provide context information for an event within the stream of incoming webhook events. They allow to have filters match when, e.g., a specific threshold of events is reached, instead of relaying each and every webhook event.

You can use each of the below listed virtual fields as the field name in any rule configuration. As virtual fields may be related to the time-series of events, you need to specify the timeframe for which the virtual field should be evaluated on. If you do not specify this value, a default of 1 hour is assumed. The time-frame is the amount of time and a unit, combined in a single string, e.g. "1h" for one hour or "30m" for 30 minutes. Available units can be found in this documentation.

Events are associated to the CFTools ID they are about and are only recognized for virtual fields of the same player. For events that may have multiple CFTools IDs (like player.kill, which has a victim and a murderer CFTools ID), the event is persisted for the CFTools ID, which matches the event the most (player.kill is an event of a kill, which is associated with the murderer). For each CFTools ID, a maximum of 100 events are preserved before the most historical events are truncated/removed.

Available Virtual Fields

Field name Explanation
vf_event_count A simple counter. Counts every event with the same type (e.g. player.kill for the specified time-frame.

Example 1: Relay kill message only, when 5 kills of the same player within the last hour

The following example will only relay the Webhook to Discord, if the murderer had 5 kills in the last hour (including the currently evaluated one):

{
  "event": "player.kill",
  "rules": [
    {
      "comparator": "gt",
      "field": "vf_event_count",
      "value": 5,
      "since": "1h"
    }
  ]
}

Example 2: Relay 5th kill message only

Based on the previous example, the set of rules in this example will only relay the 5th kill message. Kill events that appear after that are ignored.

{
  "event": "player.kill",
  "rules": [
    {
      "comparator": "gt",
      "field": "vf_event_count",
      "value": 5,
      "since": "1h"
    },
    {
      "comparator": "lt",
      "field": "vf_event_count",
      "value": 5,
      "since": "1h"
    }
  ]
}

Custom message & color

When relaying a message to your discord, CFTools Relay uses a default message, which depends on the type of event. It also chooses a default color for the embedded message, which is currently dark blue.

Optionally, you can define a custom message as well as a custom color for the message on a filter, which is used whenever the filter matches. Both options are optional and do not depend on each other. When defining a custom message for a filter, all the metadata of the message will still be relayed to your discord channel.

Color and message is only available when using the rich format type, which is the default. If you choose the text format type, the message will be relayed with as a simple text message. You may define your own template, which gives you the possibility to define what fields are forwarded in the message and in what format. See the below examples for more information about that.

Example 1: Configure a custom message for a filter

The following filter configuration will use A custom message when the first filter matches, and the default when the second matches.

  "filter": [
    {
      "event": "user.join",
      "rules": null,
      "format": {
        "type": "rich",
        "parameters": {
          "message": "A custom message"
        }
      }
    },
    {
      "event": "user.leave",
      "rules": null
    }
  ]

Example 2: Configure a custom color for a filter

The following filter configuration will use dark green as an embed color when the first filter matches, and the default when the second matches.

  "filter": [
    {
      "event": "user.join",
      "rules": null,
      "format": {
         "type": "rich",
         "parameters": {
           "color": "DARK_GREEN"
         }
      }
    },
    {
      "event": "user.leave",
      "rules": null
    }
  ]

Example 3: Using the text format for user.chat events

The following filter will use the text format type to forward user.chat events in a more readable way. The template can access all fields of the event the filter should match on (see the payloads/ directory for some example events and their available fields). For more information about the template syntax, refer to the go text/template package documentation.

  "filter": [
    {
      "event": "user.chat",
      "rules": null,
      "format": {
         "type": "text",
         "parameters": {
           "template": "[**{{.channel}}**] *{{.player_name}}*: {{.message}}"
         }
      }
    },
    {
      "event": "user.leave",
      "rules": null
    }
  ]

Custom username

When relaying messages to Discord, cftools-relay uses a default username (CFTools-Relay). If you want to use a different name instead, you can specify one individually in your filter using the username parameter.

Example 1: Using different usernames for different filters

  "filter": [
    {
      "event": "user.chat",
      "rules": null,
      "username": "SomeOtherUsername"
    },
    {
      "event": "user.leave",
      "rules": null
    }
  ]

Supported color names

You can choose from a pre-configured color palette. Supported color names can be found in the source code.