Contributing Third Party Flux Packages: A Discord Endpoint Flux Function

Navigate to:

Are you currently using Flux with InfluxDB? Have you written a great Flux function that would be useful to the community? If the answers to these questions are “Yes!”, then I encourage you to contribute your awesome work to Flux, so that others can appreciate you and your work. Today, we’re sharing how you can contribute a custom Flux function in a Third Party Flux Package.

Discord and InfluxDB

Discord, a group chatting platform like Slack, has been thought of as PC gamers’ platform until recently. With a cheaper paid plan as well as unlimited message history and storage limit, many companies are turning to Discord instead of Slack. For example, as a Developer Advocate, I’ve seen several companies and developers turn to Discord to organize virtual conferences and meetups during quarantine for these exact reasons. InfluxDB users are no exception.

Many InfluxDB users want to place Discord at the end of  their monitoring pipeline. InfluxDB allows you to monitor your data and send alerts. InfluxDB supports several notification endpoints. For example, the slack.endpoint() allows you to send critical statuses to a Slack endpoint. This blog  shows you how to contribute a Discord endpoint function. This function allows InfluxDB users to send notifications to Discord. This function was contributed by chobbs. Thank you, Craig!

How to send alerts to Discord with InfluxDB v2 Alerts

Before we learn about how to contribute a Flux package, let’s learn how to use Craig’s Discord function. This Flux script sends the last disk usage value to Discord:

import "contrib/chobbs/discord"
import "influxdata/influxdb/secrets"

//this value can be stored in the secret-store()
token = secrets.get(key: "DISCORD_TOKEN")

lastReported =
  from(bucket: "example-bucket")
    |> range(start: -1m)
    |> filter(fn: (r) => r._measurement == "statuses")
    |> last()
    |> tableFind(fn: (key) => true)
    |> getRecord(idx: 0)

discord.send(
  webhookToken:token,
  webhookID:"1234567890",
  username:"chobbs",
  content: "Great Scott!- Disk usage is: \"${lastReported.status}\".",
  avatar_url:"https://staff-photos.net/pic.jpg"

The script takes advantage of secrets.get(), to get secrets stored in InfluxDB. tableFind() extracts the first table in a stream of tables based on a group key value match, and getRecord() extracts the value - the most recent disk usage - from that table. The discord.send() function takes the webhookToken, webhookID, username, content, and avatar_url to build and send your message via HTTP POST.

How to contribute third party Flux Packages

Now that we know what our discord function does and how to use it, let’s dive into how to contribute it. These steps, package ownership rules, and future plans are described in the Contributing Third Party Flux Packages READ.md. Please make sure to view that as well.

Step One: Fork and navigate

Fork the Flux repository and clone into your $GOPATH via go get github.com/influxdata/flux. Navigate to the contrib directory in stdlib.

Step Two: Create a Flux Package Directory

Make a directory with your git account name. Within that directory, add a second directory with your Flux Package. For example, Craig created the following directory structure:

flux/stdlib/contrib/chobbs/discord

Any of your Flux code, Go code, and tests go here.

Step Three: Write Flux code

Add your Flux code, in the file discord.flux. The discord.send() function is the heart of this Flux code and package. It is a custom Flux function. Custom Flux functions use this basic structure:

// Basic function definition structure for custom Flux functions
functionName = (functionParameters) => functionOperations

The first step for our Flux code is to add the boilerplate. This includes naming our package and importing other Flux packages that we use. The naming convention is that the package name matches the Third Party Custom Package name/the directory name. Here is the boilerplate:

package discord

import "http"
import "json"

The discord.send() function takes four parameters and encodes that data as a json variable for the Discord webhook.

send = (webhookToken, webhookID, username, content, avatar_url="") => {
  data = {
      username: username,
      content: content,
      avatar_url: avatar_url
    }

  headers = {
      "Content-Type": "application/json"
    }
  encode = json.encode(v:data)

  discordURL = "https://discordapp.com/api/webhooks/"
  return http.post(headers: headers, url: discordURL + webhookID + "/" + webhookToken, data: encode)
}

The json.encode() applies the conversion to json. The function returns a http.post() function which submits HTTP POST request to the provided URL with headers specified by our discord.send() function. The http.post() function returns the HTTP status code.

Finally, let’s take a look at the complete discord.flux code.

package discord

import "http"
import "json"

// `webhookToken` - string - the secure token of the webhook.
// `webhookID` - string - the ID of the webhook.
// `username` - string - username posting the message.
// `content` - string - the text to display in discord.
// `avatar_url` -  override the default avatar of the webhook.

send = (webhookToken, webhookID, username, content, avatar_url="") => {
  data = {
      username: username,
      content: content,
      avatar_url: avatar_url
    }

  headers = {
      "Content-Type": "application/json"
    }
  encode = json.encode(v:data)

  discordURL = "https://discordapp.com/api/webhooks/"
  return http.post(headers: headers, url: discordURL + webhookID + "/" + webhookToken, data: encode)
}

Step Four: Add a Flux Test

Our test is added in discord_test.go. This test verifies that our discord.send() returns the correct status code. Name your test package. The naming convention for the test package is as follows: <custom package directory name> + "_test". Following this convention, our test package is called discord_test.

Here is the full test:

package discord_test

import (
	"context"
	"testing"

	_ "github.com/influxdata/flux/builtin"
	"github.com/influxdata/flux/dependencies/dependenciestest"
	"github.com/influxdata/flux/runtime"
)

func TestDiscord(t *testing.T) {
	ctx := dependenciestest.Default().Inject(context.Background())
	_, scope, err := runtime.Eval(ctx, `
import "contrib/chobbs/discord"
send = discord.send(webhookToken:"ThisIsAFakeToken",webhookID:"123456789",username:"chobbs",content:"this is fake content!",avatar_url:"%s/somefakeurl.com/pic.png")
send == 204
`)

	if err != nil {
		t.Error("evaluation of discord.send failed: ", err)
	}
	_ = scope
}

Step Five: Compile Flux

Now all that’s left is to compile Flux and verify that our function has been registered locally before submitting a PR.

Compiling Flux

The documentation for compiling Flux is here. However, these are the steps I took for compiling on Mac OS X.

Install the pkg-config utility:

brew install pkg-config

Install the pkg-config wrapper utility:

go get github.com/influxdata/pkg-config

Ensure the GOBIN directory is on your PATH:

export PATH=${GOPATH}/bin:${PATH}

Navigate to the Flux repository and run the following commands to build Flux:

go generate ./libflux/go/libflux
go generate ./stdlib
go build ./cmd/flux

Verify that Flux has registered your Package

You should now see that Go has auto-generated flux_gen.go. You can also verify that the Flux repl recognizes your package. Use ./flux repl to open the Flux repl. Import the discord package, and check to see if the repl autofills for discord. If successful, you’re free to try out your new function!

Flux package discord

Mad props to the InfluxData Community

InfluxData has always prided itself on having an awesome and active community. We’re grateful for all of your contributions and feedback. Whether it’s telegraf plugins, community templates, awesome InfluxDB projects, or Third Party Flux Packages, your contributions continue to both impress and humble us. I hope this tutorial empowers you to contribute your own Flux Package and share your hard work with the community. As always, please share your thoughts, concerns, or questions in the comments section, on our community site, or in our Slack channel. We’d love to get your feedback and help you with any problems you run into! Again, thank you!