mirror of
https://github.com/aramperes/ts-activity.git
synced 2025-09-08 22:08:31 -04:00
Compare commits
9 commits
Author | SHA1 | Date | |
---|---|---|---|
7aba36c064 | |||
abfc92385a | |||
9fbbd2cd22 | |||
71ed01df58 | |||
7e93beb132 | |||
db407c0ef5 | |||
71618fc8c3 | |||
976b8dc0a9 | |||
eafdbff4d5 |
10 changed files with 517 additions and 51 deletions
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
@ -32,3 +32,9 @@ jobs:
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
- name: Get the release version from the tag
|
||||||
|
if: env.VERSION == ''
|
||||||
|
run: |
|
||||||
|
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
echo "version is: ${{ env.VERSION }}"
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
.envrc
|
.envrc
|
||||||
|
/values-dev.yaml
|
||||||
|
|
51
README.md
Normal file
51
README.md
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# ts-activity
|
||||||
|
|
||||||
|
This program will post notifications to Discord when someone joins or leaves your TeamSpeak server.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
You will have to create ServerQuery credentials on an account that has permissions to login & view clients in the server. You can do this from the `Tools -> ServerQuery Login` menu in TeamSpeak 3.
|
||||||
|
|
||||||
|
This program is configured using environment variables:
|
||||||
|
|
||||||
|
- `TS_QUERY_ADDR`: Address to the TeamSpeak ServerQuery port. Example: `127.0.0.1:10011`
|
||||||
|
- `TS_QUERY_USER`: The username you selected for ServerQuery in the setup
|
||||||
|
- `TS_QUERY_PASS`: The password TeamSpeak generated for ServerQuery in the setup
|
||||||
|
- `TS_QUERY_SERVER_ID`: Virtual server ID to monitor. Defaults to `1`
|
||||||
|
- `TS_DISCORD_WEBHOOK`: Webhook URL for Discord. You can create this from the channel "Integrations" page
|
||||||
|
- `TS_DISCORD_AVATAR`: Optional URL for Discord bot avatar
|
||||||
|
- `TS_DISCORD_USERNAME`: Optional nickname for Discord bot
|
||||||
|
|
||||||
|
## Run it
|
||||||
|
[](https://hub.docker.com/r/aramperes/ts-activity)
|
||||||
|
|
||||||
|
To build and run locally:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go mod download
|
||||||
|
go run .
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, using the [Docker image](https://hub.docker.com/r/aramperes/ts-activity):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run --rm --name ts-activity \
|
||||||
|
-e TS_DISCORD_WEBHOOK='https://discord.com/api/webhooks/...' \
|
||||||
|
-e TS_QUERY_ADDR=127.0.0.1:10011 \
|
||||||
|
-e TS_QUERY_USER=Jeff \
|
||||||
|
-e TS_QUERY_PASS=******* \
|
||||||
|
aramperes/ts-activity
|
||||||
|
```
|
||||||
|
|
||||||
|
There is also a Helm chart. You can create a `Secret` containing `username`, `password`, and `discord`, and then:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
helm upgrade --install ts-activity momoperes/ts-activity \
|
||||||
|
--set config.serverQueryAddr=teamspeak:10011 \
|
||||||
|
--set config.discordUsername=Jeff \
|
||||||
|
--set config.serverQuerySecret=ts-activity \
|
||||||
|
--set config.webhookSecret=ts-activity
|
||||||
|
```
|
262
cmd.go
262
cmd.go
|
@ -1,35 +1,115 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gtuk/discordwebhook"
|
"github.com/gtuk/discordwebhook"
|
||||||
"github.com/multiplay/go-ts3"
|
"github.com/multiplay/go-ts3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// App holds the configuration
|
||||||
|
type App struct {
|
||||||
|
discordURL string
|
||||||
|
discordUsername string
|
||||||
|
discordAvatarURL *string
|
||||||
|
tsQueryAddr string
|
||||||
|
tsQueryUser string
|
||||||
|
tsQueryPass string
|
||||||
|
tsQueryServerID int
|
||||||
|
spotLightGfxFormat string
|
||||||
|
spotLightIDMap map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func appFromEnv() (*App, error) {
|
||||||
|
discordURL := os.Getenv("TS_DISCORD_WEBHOOK")
|
||||||
|
if discordURL == "" {
|
||||||
|
return nil, errors.New("must configure: TS_DISCORD_WEBHOOK")
|
||||||
|
}
|
||||||
|
discordUsername := os.Getenv("TS_DISCORD_USERNAME")
|
||||||
|
if discordUsername == "" {
|
||||||
|
discordUsername = "TeamSpeak"
|
||||||
|
}
|
||||||
|
|
||||||
|
var discordAvatarURL *string = nil
|
||||||
|
if val, ok := os.LookupEnv("TS_DISCORD_AVATAR"); ok {
|
||||||
|
discordAvatarURL = &val
|
||||||
|
}
|
||||||
|
|
||||||
|
tsQueryAddr := os.Getenv("TS_QUERY_ADDR")
|
||||||
|
if tsQueryAddr == "" {
|
||||||
|
return nil, errors.New("must configure: TS_QUERY_ADDR")
|
||||||
|
}
|
||||||
|
tsQueryUser := os.Getenv("TS_QUERY_USER")
|
||||||
|
if tsQueryUser == "" {
|
||||||
|
return nil, errors.New("must configure: TS_QUERY_USER")
|
||||||
|
}
|
||||||
|
tsQueryPass := os.Getenv("TS_QUERY_PASS")
|
||||||
|
if tsQueryPass == "" {
|
||||||
|
return nil, errors.New("must configure: TS_QUERY_PASS")
|
||||||
|
}
|
||||||
|
tsQueryServerID := 1
|
||||||
|
if val, ok := os.LookupEnv("TS_QUERY_SERVER_ID"); ok {
|
||||||
|
val, err := strconv.Atoi(val)
|
||||||
|
if err == nil {
|
||||||
|
tsQueryServerID = val
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("invalid TS_QUERY_SERVER_ID, must be int")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spotLightGfxFormat := os.Getenv("TS_SPOTLIGHT_GFX_FMT")
|
||||||
|
|
||||||
|
// TODO: Load from environment variable
|
||||||
|
spotLightIDMap := make(map[string]int)
|
||||||
|
spotLightIDMap["rb+mT/4bh37gHzQYqTgBiEHG2IA="] = 0
|
||||||
|
spotLightIDMap["sA3fHhvqmlSuFYtMoVYseRQI2DE="] = 0
|
||||||
|
spotLightIDMap["9K6JV7kWaRU+4HFRkXrBZNjSmRA="] = 1
|
||||||
|
spotLightIDMap["pFclzBx0w2UmwPd91VvaXJjYCYA="] = 2
|
||||||
|
spotLightIDMap["tvjlpKqvcyQSCCVkT0TJ28uwdaQ="] = 3
|
||||||
|
spotLightIDMap["SLLvtjVBmSoIzpMhlxnLa9CWoOU="] = 4
|
||||||
|
spotLightIDMap["7EU/Up++D9+8SQk0sNchEuKPufw="] = 5
|
||||||
|
spotLightIDMap["SyldxnLYWHJOUj3HnEsXGF6B0T4="] = 5
|
||||||
|
spotLightIDMap["Mc/TdoNhddKdGtB55DSZrYk3NWc="] = 6
|
||||||
|
spotLightIDMap["xOWMWG/TpkbV8XjahqqoQLsHHpA="] = 7
|
||||||
|
spotLightIDMap["G4kg1LKJElM5LIpoeA6gN7DMl0c="] = 7
|
||||||
|
spotLightIDMap["wuQ907NtzqL4uxhLk3P/TCpkXF0="] = 8
|
||||||
|
|
||||||
|
return &App{
|
||||||
|
discordURL: discordURL,
|
||||||
|
discordUsername: discordUsername,
|
||||||
|
discordAvatarURL: discordAvatarURL,
|
||||||
|
tsQueryAddr: tsQueryAddr,
|
||||||
|
tsQueryUser: tsQueryUser,
|
||||||
|
tsQueryPass: tsQueryPass,
|
||||||
|
tsQueryServerID: tsQueryServerID,
|
||||||
|
spotLightGfxFormat: spotLightGfxFormat,
|
||||||
|
spotLightIDMap: spotLightIDMap,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
discord := os.Getenv("TS_DISCORD_WEBHOOK")
|
app, err := appFromEnv()
|
||||||
if discord == "" {
|
if err != nil {
|
||||||
log.Fatal("Must configure: TS_DISCORD_WEBHOOK")
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect and login
|
// Connect and login
|
||||||
c, err := ts3.NewClient(os.Getenv("TS_QUERY_ADDR"))
|
c, err := ts3.NewClient(app.tsQueryAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
user := os.Getenv("TS_QUERY_USER")
|
if err := c.Login(app.tsQueryUser, app.tsQueryPass); err != nil {
|
||||||
pass := os.Getenv("TS_QUERY_PASS")
|
|
||||||
if err := c.Login(user, pass); err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.Use(1); err != nil {
|
if err := c.Use(app.tsQueryServerID); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +130,9 @@ func main() {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientMap := make(map[int]string)
|
clientMap := make(map[string]string)
|
||||||
|
clientDatabaseIDs := make(map[string]string)
|
||||||
|
clientUniqueIDs := make(map[string]string)
|
||||||
|
|
||||||
log.Println("Online clients:")
|
log.Println("Online clients:")
|
||||||
for _, client := range cl {
|
for _, client := range cl {
|
||||||
|
@ -58,88 +140,166 @@ func main() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Println("-", client)
|
log.Println("-", client)
|
||||||
clientMap[client.ID] = client.Nickname
|
clientMap[strconv.Itoa(client.ID)] = client.Nickname
|
||||||
|
clientDatabaseIDs[strconv.Itoa(client.ID)] = strconv.Itoa(client.DatabaseID)
|
||||||
|
|
||||||
|
uid, err := getClientUniqueId(c, strconv.Itoa(client.DatabaseID))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientUniqueIDs[strconv.Itoa(client.ID)] = uid
|
||||||
|
log.Println(" - UID:", uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the banner on startup with the currently online users.
|
||||||
|
app.updateSpotLight(c, mapValues(clientUniqueIDs))
|
||||||
|
|
||||||
// Listen for client updates
|
// Listen for client updates
|
||||||
notifs := c.Notifications()
|
notifs := c.Notifications()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
event := <-notifs
|
event := <-notifs
|
||||||
log.Println("=>", event)
|
|
||||||
|
|
||||||
if event.Type == "cliententerview" {
|
if event.Type == "cliententerview" {
|
||||||
if event.Data["client_type"] != "0" {
|
if event.Data["client_type"] != "0" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
clientId, err := strconv.Atoi(event.Data["clid"])
|
clientID, ok := event.Data["clid"]
|
||||||
if err != nil {
|
if !ok {
|
||||||
log.Println("Failed to get client ID:", err)
|
log.Println("User has no client id", event.Data)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
clientDBID, ok := event.Data["client_database_id"]
|
||||||
|
if !ok {
|
||||||
|
log.Println("User has no client database id", event.Data)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
clientNick, ok := event.Data["client_nickname"]
|
clientNick, ok := event.Data["client_nickname"]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Println("User has no nickname:", clientId)
|
log.Println("User has no nickname:", clientID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, previous := clientMap[clientId]
|
_, previous := clientMap[clientID]
|
||||||
clientMap[clientId] = clientNick
|
clientMap[clientID] = clientNick
|
||||||
|
|
||||||
if !previous {
|
if !previous {
|
||||||
ClientConnected(discord, clientNick)
|
app.clientConnected(clientNick)
|
||||||
|
|
||||||
|
clientDatabaseIDs[clientID] = clientDBID
|
||||||
|
uid, err := getClientUniqueId(c, clientDBID)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientUniqueIDs[clientID] = uid
|
||||||
|
|
||||||
|
app.updateSpotLight(c, mapValues(clientUniqueIDs))
|
||||||
}
|
}
|
||||||
} else if event.Type == "clientleftview" {
|
} else if event.Type == "clientleftview" {
|
||||||
clientId, err := strconv.Atoi(event.Data["clid"])
|
clientID, ok := event.Data["clid"]
|
||||||
if err != nil {
|
|
||||||
log.Println("Failed to get client ID:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
clientNick, ok := clientMap[clientId]
|
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Println("Unknown user left:", clientId)
|
log.Println("User has no client id", event.Data)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(clientMap, clientId)
|
clientNick, ok := clientMap[clientID]
|
||||||
ClientDisconnected(discord, clientNick)
|
if !ok {
|
||||||
|
log.Println("Unknown user left:", clientID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(clientMap, clientID)
|
||||||
|
delete(clientDatabaseIDs, clientID)
|
||||||
|
delete(clientUniqueIDs, clientID)
|
||||||
|
app.updateSpotLight(c, mapValues(clientUniqueIDs))
|
||||||
|
app.clientDisconnected(clientNick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClientConnected(discord string, nick string) {
|
func (app *App) sendWebhook(content string) {
|
||||||
bot := os.Getenv("TS_DISCORD_USERNAME")
|
message := discordwebhook.Message{
|
||||||
if bot == "" {
|
Username: &app.discordUsername,
|
||||||
bot = "Jeff"
|
Content: &content,
|
||||||
|
AvatarUrl: app.discordAvatarURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := discordwebhook.SendMessage(app.discordURL, message); err != nil {
|
||||||
|
log.Println("Failed to log Discord message:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) clientConnected(nick string) {
|
||||||
content := fmt.Sprintf("Client connected: %s", nick)
|
content := fmt.Sprintf("Client connected: %s", nick)
|
||||||
message := discordwebhook.Message{
|
app.sendWebhook(content)
|
||||||
Username: &bot,
|
|
||||||
Content: &content,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := discordwebhook.SendMessage(discord, message); err != nil {
|
|
||||||
log.Println("Failed to log Discord message:", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClientDisconnected(discord string, nick string) {
|
func (app *App) clientDisconnected(nick string) {
|
||||||
bot := os.Getenv("TS_DISCORD_USERNAME")
|
|
||||||
if bot == "" {
|
|
||||||
bot = "Jeff"
|
|
||||||
}
|
|
||||||
|
|
||||||
content := fmt.Sprintf("Client disconnected: %s", nick)
|
content := fmt.Sprintf("Client disconnected: %s", nick)
|
||||||
message := discordwebhook.Message{
|
app.sendWebhook(content)
|
||||||
Username: &bot,
|
}
|
||||||
Content: &content,
|
|
||||||
|
func (app *App) updateSpotLight(c *ts3.Client, connectedUIDs []string) {
|
||||||
|
if app.spotLightGfxFormat == "" {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := discordwebhook.SendMessage(discord, message); err != nil {
|
spotLightIDs := make([]int, 0)
|
||||||
log.Println("Failed to log Discord message:", err)
|
for _, uid := range connectedUIDs {
|
||||||
|
spotLightID, ok := app.spotLightIDMap[uid]
|
||||||
|
if ok {
|
||||||
|
spotLightIDs = append(spotLightIDs, spotLightID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(spotLightIDs) == 0 {
|
||||||
|
updateBanner(c, fmt.Sprintf(app.spotLightGfxFormat, "empty"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.Sort(spotLightIDs)
|
||||||
|
slices.Compact(spotLightIDs)
|
||||||
|
|
||||||
|
spotLightIDStrings := make([]string, len(spotLightIDs))
|
||||||
|
for idx, id := range spotLightIDs {
|
||||||
|
spotLightIDStrings[idx] = strconv.Itoa(id)
|
||||||
|
}
|
||||||
|
joined := strings.Join(spotLightIDStrings, "_")
|
||||||
|
|
||||||
|
updateBanner(c, fmt.Sprintf(app.spotLightGfxFormat, joined))
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateBanner(c *ts3.Client, gfx string) {
|
||||||
|
err := c.Server.Edit(ts3.NewArg("virtualserver_hostbanner_gfx_url", gfx))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to update banner:", gfx, err)
|
||||||
|
} else {
|
||||||
|
log.Println("Updated banner:", gfx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getClientUniqueId(c *ts3.Client, dbID string) (string, error) {
|
||||||
|
var uid = struct {
|
||||||
|
UID string `ms:"cluid"`
|
||||||
|
}{}
|
||||||
|
_, err := c.ExecCmd(ts3.NewCmd("clientgetnamefromdbid").WithArgs(ts3.NewArg("cldbid", dbID)).WithResponse(&uid))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return uid.UID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapValues(m map[string]string) []string {
|
||||||
|
v := make([]string, 0, len(m))
|
||||||
|
for _, val := range m {
|
||||||
|
v = append(v, val)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
23
helm/ts-activity/.helmignore
Normal file
23
helm/ts-activity/.helmignore
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Patterns to ignore when building packages.
|
||||||
|
# This supports shell glob matching, relative path matching, and
|
||||||
|
# negation (prefixed with !). Only one pattern per line.
|
||||||
|
.DS_Store
|
||||||
|
# Common VCS dirs
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.bzr/
|
||||||
|
.bzrignore
|
||||||
|
.hg/
|
||||||
|
.hgignore
|
||||||
|
.svn/
|
||||||
|
# Common backup files
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.orig
|
||||||
|
*~
|
||||||
|
# Various IDEs
|
||||||
|
.project
|
||||||
|
.idea/
|
||||||
|
*.tmproj
|
||||||
|
.vscode/
|
8
helm/ts-activity/Chart.yaml
Normal file
8
helm/ts-activity/Chart.yaml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
apiVersion: v2
|
||||||
|
name: ts-activity
|
||||||
|
description: Post TeamSpeak events to Discord
|
||||||
|
|
||||||
|
type: application
|
||||||
|
|
||||||
|
version: "0.0.0"
|
||||||
|
appVersion: "v0.0.0"
|
62
helm/ts-activity/templates/_helpers.tpl
Normal file
62
helm/ts-activity/templates/_helpers.tpl
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
{{/*
|
||||||
|
Expand the name of the chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "ts-activity.name" -}}
|
||||||
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create a default fully qualified app name.
|
||||||
|
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||||
|
If release name contains chart name it will be used as a full name.
|
||||||
|
*/}}
|
||||||
|
{{- define "ts-activity.fullname" -}}
|
||||||
|
{{- if .Values.fullnameOverride }}
|
||||||
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||||
|
{{- if contains $name .Release.Name }}
|
||||||
|
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create chart name and version as used by the chart label.
|
||||||
|
*/}}
|
||||||
|
{{- define "ts-activity.chart" -}}
|
||||||
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Common labels
|
||||||
|
*/}}
|
||||||
|
{{- define "ts-activity.labels" -}}
|
||||||
|
helm.sh/chart: {{ include "ts-activity.chart" . }}
|
||||||
|
{{ include "ts-activity.selectorLabels" . }}
|
||||||
|
{{- if .Chart.AppVersion }}
|
||||||
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||||
|
{{- end }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Selector labels
|
||||||
|
*/}}
|
||||||
|
{{- define "ts-activity.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "ts-activity.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the service account to use
|
||||||
|
*/}}
|
||||||
|
{{- define "ts-activity.serviceAccountName" -}}
|
||||||
|
{{- if .Values.serviceAccount.create }}
|
||||||
|
{{- default (include "ts-activity.fullname" .) .Values.serviceAccount.name }}
|
||||||
|
{{- else }}
|
||||||
|
{{- default "default" .Values.serviceAccount.name }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
75
helm/ts-activity/templates/deployment.yaml
Normal file
75
helm/ts-activity/templates/deployment.yaml
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "ts-activity.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "ts-activity.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "ts-activity.selectorLabels" . | nindent 6 }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
{{- with .Values.podAnnotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
{{- include "ts-activity.selectorLabels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "ts-activity.serviceAccountName" . }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||||
|
containers:
|
||||||
|
- name: {{ .Chart.Name }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||||
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
env:
|
||||||
|
{{- with .Values.config.discordUsername }}
|
||||||
|
- name: TS_DISCORD_USERNAME
|
||||||
|
value: {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.config.discordAvatar }}
|
||||||
|
- name: TS_DISCORD_AVATAR
|
||||||
|
value: {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
- name: TS_QUERY_SERVER_ID
|
||||||
|
value: {{ .Values.config.serverQueryId | quote }}
|
||||||
|
- name: TS_QUERY_ADDR
|
||||||
|
value: "{{ .Values.config.serverQueryAddr | required "must provide serverQueryAddr" }}"
|
||||||
|
- name: TS_QUERY_USER
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: username
|
||||||
|
name: "{{ .Values.config.serverQuerySecret | required "must provide serverQuerySecret" }}"
|
||||||
|
- name: TS_QUERY_PASS
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: password
|
||||||
|
name: "{{ .Values.config.serverQuerySecret | required "must provide serverQuerySecret" }}"
|
||||||
|
- name: TS_DISCORD_WEBHOOK
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: discord
|
||||||
|
name: "{{ .Values.config.webhookSecret | required "must provide webhookSecret" }}"
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
|
{{- with .Values.nodeSelector }}
|
||||||
|
nodeSelector:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.affinity }}
|
||||||
|
affinity:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.tolerations }}
|
||||||
|
tolerations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
12
helm/ts-activity/templates/serviceaccount.yaml
Normal file
12
helm/ts-activity/templates/serviceaccount.yaml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{{- if .Values.serviceAccount.create -}}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: {{ include "ts-activity.serviceAccountName" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "ts-activity.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.serviceAccount.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
68
helm/ts-activity/values.yaml
Normal file
68
helm/ts-activity/values.yaml
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Default values for ts-activity.
|
||||||
|
# This is a YAML-formatted file.
|
||||||
|
# Declare variables to be passed into your templates.
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: aramperes/ts-activity
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
# Overrides the image tag whose default is the chart appVersion.
|
||||||
|
tag: ""
|
||||||
|
|
||||||
|
config:
|
||||||
|
# Discord username displayed in webhook messages.
|
||||||
|
# Defaults to 'Jeff'
|
||||||
|
discordUsername: ""
|
||||||
|
# Discord avatar displayed in webhook messages.
|
||||||
|
discordAvatar: ""
|
||||||
|
# Address to plain ServerQuery. Usually <ts_host>:10011
|
||||||
|
serverQueryAddr: ""
|
||||||
|
# Secret containing 'username' and 'password' for ServerQuery.
|
||||||
|
serverQuerySecret: ""
|
||||||
|
# Secret containing 'discord' with the Webhook URL.
|
||||||
|
webhookSecret: ""
|
||||||
|
# TeamSpeak virtual server ID.
|
||||||
|
serverQueryId: 1
|
||||||
|
|
||||||
|
imagePullSecrets: []
|
||||||
|
nameOverride: ""
|
||||||
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
serviceAccount:
|
||||||
|
# Specifies whether a service account should be created
|
||||||
|
create: true
|
||||||
|
# Annotations to add to the service account
|
||||||
|
annotations: {}
|
||||||
|
# The name of the service account to use.
|
||||||
|
# If not set and create is true, a name is generated using the fullname template
|
||||||
|
name: ""
|
||||||
|
|
||||||
|
podAnnotations: {}
|
||||||
|
|
||||||
|
podSecurityContext: {}
|
||||||
|
# fsGroup: 2000
|
||||||
|
|
||||||
|
securityContext: {}
|
||||||
|
# capabilities:
|
||||||
|
# drop:
|
||||||
|
# - ALL
|
||||||
|
# readOnlyRootFilesystem: true
|
||||||
|
# runAsNonRoot: true
|
||||||
|
# runAsUser: 1000
|
||||||
|
|
||||||
|
resources: {}
|
||||||
|
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||||
|
# choice for the user. This also increases chances charts run on environments with little
|
||||||
|
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||||
|
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||||
|
# limits:
|
||||||
|
# cpu: 100m
|
||||||
|
# memory: 128Mi
|
||||||
|
# requests:
|
||||||
|
# cpu: 100m
|
||||||
|
# memory: 128Mi
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
|
||||||
|
tolerations: []
|
||||||
|
|
||||||
|
affinity: {}
|
Loading…
Add table
Add a link
Reference in a new issue