diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6a99888
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+docs/static/* filter=lfs diff=lfs merge=lfs -text
diff --git a/.github/workflows/doc-generator.yml b/.github/workflows/doc-generator.yml
new file mode 100644
index 0000000..179aa93
--- /dev/null
+++ b/.github/workflows/doc-generator.yml
@@ -0,0 +1,57 @@
+---
+
+name: doc-generator
+on: push
+
+jobs:
+ doc-generator:
+ defaults:
+ run:
+ shell: bash
+
+ container:
+ image: luzifer/archlinux
+ env:
+ CGO_ENABLED: 0
+ GOPATH: /go
+
+ permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+ runs-on: ubuntu-latest
+
+ steps:
+
+ - name: Install required packages
+ run: |
+ pacman -Syy --noconfirm \
+ curl \
+ git \
+ git-lfs \
+ make \
+ tar
+
+ - uses: actions/checkout@v3
+ with:
+ lfs: true
+ submodules: true
+
+ - name: Marking workdir safe
+ run: git config --global --add safe.directory /__w/twitch-bot/twitch-bot
+
+ - name: Generate documentation
+ run: make render_docs
+
+ - name: Upload GitHub Pages artifact
+ if: github.ref == 'refs/heads/master'
+ uses: actions/upload-pages-artifact@v1
+ with:
+ path: .rendered-docs
+
+ - name: Deploy artifact
+ if: github.ref == 'refs/heads/master'
+ uses: actions/deploy-pages@v1
+
+...
diff --git a/.github/workflows/wiki-update.yml b/.github/workflows/wiki-update.yml
deleted file mode 100644
index 94066eb..0000000
--- a/.github/workflows/wiki-update.yml
+++ /dev/null
@@ -1,38 +0,0 @@
----
-
-name: wiki-update
-on:
- push:
- branches:
- - master
-
-jobs:
- wiki-update:
- defaults:
- run:
- shell: bash
-
- container:
- image: luzifer/archlinux
- env:
- CGO_ENABLED: 0
- GOPATH: /go
-
- runs-on: ubuntu-latest
-
- steps:
-
- - name: Install required packages
- run: |
- pacman -Syy --noconfirm \
- git
-
- - uses: actions/checkout@v3
-
- - name: Marking workdir safe
- run: git config --global --add safe.directory /__w/twitch-bot/twitch-bot
-
- - name: Update Wiki
- run: 'git push https://github.com/Luzifer/twitch-bot.wiki.git $(git subtree split --prefix wiki HEAD):refs/heads/master --force'
-
-...
diff --git a/.gitignore b/.gitignore
index 0ed644a..29c556c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,15 @@
config
config.hcl
config.yaml
+docs/resources/_gen
editor/app.css
editor/app.js
editor/bundle.*
.env
+hugo_*
+.hugo_build.lock
node_modules
+.rendered-docs
storage.db
storage.db-journal
storage.json.gz
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..9142f5f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "docs/themes/ace-documentation"]
+ path = docs/themes/ace-documentation
+ url = https://github.com/vantagedesign/ace-documentation.git
diff --git a/Makefile b/Makefile
index 67a09c0..7c82cfd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,13 @@
+HUGO_VERSION:=0.117.0
+
default: lint frontend_lint test
+build_prod: frontend_prod
+ go build \
+ -trimpath \
+ -mod=readonly \
+ -ldflags "-X main.version=$(shell git describe --tags --always || echo dev)"
+
lint:
golangci-lint run
@@ -27,17 +35,6 @@ frontend_lint: node_modules
node_modules:
npm ci
-# --- Wiki Updates
-
-actor_docs:
- go run . --storage-conn-string $(shell mktemp).db actor-docs >wiki/Actors.md
-
-pull_wiki:
- git subtree pull --prefix=wiki https://github.com/Luzifer/twitch-bot.wiki.git master --squash
-
-push_wiki:
- git subtree push --prefix=wiki https://github.com/Luzifer/twitch-bot.wiki.git master
-
# --- Tools
update_ua_list:
@@ -56,3 +53,19 @@ trivy:
--scanners config,license,secret,vuln \
--severity HIGH,CRITICAL \
--skip-dirs docs
+
+# -- Documentation Site --
+
+actor_docs:
+ go run . --storage-conn-string $(shell mktemp).db actor-docs >docs/content/configuration/actors.md
+
+eventclient_docs:
+ echo -e "---\ntitle: EventClient\nweight: 10000\n---\n" >docs/content/overlays/eventclient.md
+ docker run --rm -i -v $(CURDIR):$(CURDIR) -w $(CURDIR) node:18-alpine sh -ec 'npx --yes jsdoc-to-markdown --files ./internal/apimodules/overlays/default/eventclient.js' >>docs/content/overlays/eventclient.md
+
+render_docs: hugo_$(HUGO_VERSION)
+ ./hugo_$(HUGO_VERSION) --cleanDestinationDir --gc --source docs
+
+hugo_$(HUGO_VERSION):
+ curl -sSfL https://github.com/gohugoio/hugo/releases/download/v$(HUGO_VERSION)/hugo_extended_$(HUGO_VERSION)_linux-amd64.tar.gz | tar -xz hugo
+ mv hugo $@
diff --git a/actorDocs.tpl b/actorDocs.tpl
index fd37855..91b7fd2 100644
--- a/actorDocs.tpl
+++ b/actorDocs.tpl
@@ -1,4 +1,10 @@
-# Available Actions
+---
+title: "Available Actions"
+---
+
+{{ "{{< lead >}}" }}
+All these actions can be executed by your bot as soon as you add them to rules. Read their documentation to learn how to master them.
+{{ "{{< /lead >}}" }}
{{ range .Actors }}
## {{ .Name }}
diff --git a/docs/archetypes/default.md b/docs/archetypes/default.md
new file mode 100644
index 0000000..00e77bd
--- /dev/null
+++ b/docs/archetypes/default.md
@@ -0,0 +1,6 @@
+---
+title: "{{ replace .Name "-" " " | title }}"
+date: {{ .Date }}
+draft: true
+---
+
diff --git a/docs/content/_index.md b/docs/content/_index.md
new file mode 100644
index 0000000..462acd5
--- /dev/null
+++ b/docs/content/_index.md
@@ -0,0 +1,19 @@
+---
+title: Twitch-Bot Documentation
+---
+
+{{< lead >}}
+You are tired of all those cloud-bots working only sometimes, messing up at random times (always when you need them most) and limiting you to very little functionality while forcing you to give them every single possible permission available? Twitch-Bot is a **fully open-source** bot you can host yourself giving you **full control** over its functions and being **extensible** using custom scripts and commands any developer can build for you. Additionally you are in control which **permissions** to give to the bot and which you rather not want it to have.
+{{< /lead >}}
+
+## Features
+
+**Open-Source:** This means you (or any developer you trust) can look up how things work inside the bot, can modify the bot yourself and be sure the bot does not use the access you are granting it to do stuff you don't want it to do. Also you are not dependent on some company to keep the bot running for you but are in control over it. In case I'm no longer willing to develop the bot, it will not cease to exist but can be developed further by anyone.
+
+**Overlays:** The bot contains a web-server to host custom overlays which can be built like any website with **HTML and Javascript**. Some default overlays are included ready to use and for everything not available in the default distribution there is a helper library available to connect to the bot and work with events and bot state.
+
+**YAML Configuration:** The whole configuration is stored in a single YAML file, not in any proprietary format. You can simply create a backup of that file and even if some mistake or broken server happens you simply put back the configuration file and all of your rules, auto-messages, API-keys are instantly back again. (What's not in the configuration file is the data the bot stores like counters, events and variables.)
+
+**Common Database Formats:** All the data mentioned in the last point is stored in a common database format like **SQLite**, **MySQL** or **PostgreSQL**. With exception of the credentials all the data is stored in a plain format which means you can use well-known database tooling to create backups. This also means you can use custom tooling to do with the data what you want! (Though a warning for this point: The database schema is not guaranteed to be stable! While it's possible I do not recommend directly accessing the data in the database for other tools.)
+
+**API-First Design:** The bot is built to have an API and an included documentation for this API. Most of its functionality is exposed through the API and you can easily build tooling against that API to make to bot do your bidding.
diff --git a/docs/content/configuration/_index.md b/docs/content/configuration/_index.md
new file mode 100644
index 0000000..e54ebb9
--- /dev/null
+++ b/docs/content/configuration/_index.md
@@ -0,0 +1,15 @@
+---
+title: "Configuration"
+weight: 2
+---
+
+{{< lead >}}
+As the bot is capable of doing a lot of things, the configuration needs to cover all of this and therefore isn't simple to master. This section is to help you mastering your configuration and to help you to get your bot to speed.
+{{< /lead >}}
+
+## What to do here?
+
+- Have a look at the [Config-File Syntax]({{< ref "config-file.md" >}}) to get an overview what you can do in your `config.yaml`
+- Read about the [Available Actions]({{< ref "actors.md" >}}) to see what the bot is capable of
+- See [Available Events]({{< ref "events.md" >}}) to get an overview on which events you can react with those actions
+- Learn about [Templating]({{< ref "templating.md" >}}) for knowledge about the template syntax and available functions
diff --git a/wiki/Actors.md b/docs/content/configuration/actors.md
similarity index 96%
rename from wiki/Actors.md
rename to docs/content/configuration/actors.md
index 5cda458..772bc82 100644
--- a/wiki/Actors.md
+++ b/docs/content/configuration/actors.md
@@ -1,4 +1,10 @@
-# Available Actions
+---
+title: "Available Actions"
+---
+
+{{< lead >}}
+All these actions can be executed by your bot as soon as you add them to rules. Read their documentation to learn how to master them.
+{{< /lead >}}
## Add Fields to Event
@@ -396,7 +402,11 @@ Scans for links in the message and adds the "links" field to the event data
```yaml
- type: linkdetector
- # Does not have configuration attributes
+ attributes:
+ # Enable heuristic scans to find links with spaces or other means of obfuscation in them
+ # Optional: true
+ # Type: bool
+ heuristic: false
```
## Send RAW Message
diff --git a/wiki/Home.md b/docs/content/configuration/config-file.md
similarity index 81%
rename from wiki/Home.md
rename to docs/content/configuration/config-file.md
index c23df3e..f43a525 100644
--- a/wiki/Home.md
+++ b/docs/content/configuration/config-file.md
@@ -1,4 +1,10 @@
-## Configuration
+---
+title: "Config-File Syntax"
+---
+
+{{< lead >}}
+The YAML configuration file is the heart of the bot configuration. You can configure every aspect of the bot using the configuration file. The web-interface afterwards allows to modify the configuration file to assist you with the configuration.
+{{< /lead >}}
```yaml
---
@@ -139,49 +145,3 @@ rules: # See below for examples
...
```
-
-## Command executions
-
-Your command will get a JSON object passed through `stdin` you can parse to gain details about the message. It is expected to yield an array of actions on `stdout` and exit with status `0`. If it does not the action will be marked failed. In case you need to output debug output you can use `stderr` which is directly piped to the bots `stderr`.
-
-This is an example input you might get on `stdin`:
-
-```json
-{
- "badges": {
- "glhf-pledge": 1,
- "moderator": 1
- },
- "channel": "#tezrian",
- "message": "!test",
- "tags": {
- "badge-info": "",
- "badges": "moderator/1,glhf-pledge/1",
- "client-nonce": "6801c82a341f728dbbaad87ef30eae49",
- "color": "#A72920",
- "display-name": "Luziferus",
- "emotes": "",
- "flags": "",
- "id": "dca06466-3741-4b22-8339-4cb5b07a02cc",
- "mod": "1",
- "room-id": "485884564",
- "subscriber": "0",
- "tmi-sent-ts": "1610313040489",
- "turbo": "0",
- "user-id": "69699328",
- "user-type": "mod"
- },
- "username": "luziferus"
-}
-```
-
-The example was dumped using this action:
-
-```yaml
- - actions:
- - type: script
- attributes:
- command: [/usr/bin/bash, -c, "jq . >&2"]
- match_channels: ['#tezrian']
- match_message: '^!test'
-```
diff --git a/wiki/Events.md b/docs/content/configuration/events.md
similarity index 99%
rename from wiki/Events.md
rename to docs/content/configuration/events.md
index 59c317f..cc87a53 100644
--- a/wiki/Events.md
+++ b/docs/content/configuration/events.md
@@ -1,4 +1,6 @@
-# Available Events
+---
+title: "Available Events"
+---
## `ban`
diff --git a/wiki/Examples.md b/docs/content/configuration/rule-examples.md
similarity index 64%
rename from wiki/Examples.md
rename to docs/content/configuration/rule-examples.md
index a2cb6c3..55f90b3 100644
--- a/wiki/Examples.md
+++ b/docs/content/configuration/rule-examples.md
@@ -1,42 +1,58 @@
-# Rule examples
+---
+title: "Rule Examples"
+---
## Chat-addable generic text-respond-commands
```yaml
- # Respond with variable content if set
- - actions:
+- uuid: 688e631f-08a8-5544-b4b2-1737ea71ce00
+ description: Trigger Generic Command
+ actions:
- type: respond
attributes:
message: '{{ variable (list "genericcmd" .channel (group 1) | join ":") }}'
- disable_on_template: '{{ eq (variable (list "genericcmd" .channel (group 1) | joih ":")) "" }}'
- match_channels: ['#mychannel']
- match_message: '^!([^\s]+)(?: |$)'
+ cooldown: 1m0s
+ match_channels:
+ - '#luziferus'
+ - '#tezrian'
+ match_message: '^!([^\s]+)(?: |$)'
+ disable_on_template: '{{ eq (variable (list "genericcmd" .channel (group 1) | join ":")) "" }}'
- # Set variable content to content of chat command
- - actions:
+- uuid: ba4f7bb3-af39-5c57-bb97-216a8af69246
+ description: Set Generic Command
+ actions:
- type: setvariable
attributes:
- variable: '{{ list "genericcmd" .channel (group 1) | join ":" }}'
set: '{{ group 2 }}'
+ variable: '{{ list "genericcmd" .channel (group 1) | join ":" }}'
- type: respond
attributes:
message: '[Admin] Set command !{{ group 1 }} to "{{ group 2 }}"'
- enable_on: [broadcaster, moderator]
- match_channels: ['#mychannel']
- match_message: '^!setcmd ([^\s]+) (.*)'
+ match_channels:
+ - '#luziferus'
+ - '#tezrian'
+ match_message: ^!setcmd ([^\s]+) (.*)
+ enable_on:
+ - broadcaster
+ - moderator
- # Remove variable and therefore delete command
- - actions:
+- uuid: 21619e80-2c6a-536e-8b83-e5fe6c580356
+ description: Clear Generic Command
+ actions:
- type: setvariable
attributes:
- variable: '{{ list "genericcmd" .channel (group 1) | join ":" }}'
clear: true
+ variable: '{{ list "genericcmd" .channel (group 1) | join ":" }}'
- type: respond
attributes:
message: '[Admin] Deleted command !{{ group 1 }}'
- enable_on: [broadcaster, moderator]
- match_channels: ['#mychannel']
- match_message: '^!clearcmd ([^\s]+)'
+ match_channels:
+ - '#luziferus'
+ - '#tezrian'
+ match_message: ^!clearcmd ([^\s]+)
+ enable_on:
+ - broadcaster
+ - moderator
```
## Game death counter with dynamic name
@@ -57,24 +73,6 @@
match_message: '^!death'
```
-## Link-protection while allowing Twitch clips
-
-```yaml
- - actions:
- - type: timeout
- attributes:
- duration: 1s
- - type: respond
- attributes:
- message: '@{{ .username }}, please ask for permission before posting links.'
- disable_on: [broadcaster, moderator, subscriber, vip]
- disable_on_match_messages:
- - '^(?:https?://)?clips\.twitch\.tv/[a-zA-Z0-9-]+$'
- disable_on_permit: true
- match_channels: ['#mychannel']
- match_message: '(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]'
-```
-
## Post follow date for an user
```yaml
@@ -108,9 +106,7 @@
- actions:
- type: respond
attributes:
- message: >-
- @{{ fixUsername (arg 1) }}, you will not get timed out
- for the next {{ .permitTimeout }} seconds.
+ message: '{{ mention .to }}, you will not get timed out for the next {{ .permitTimeout }} seconds.'
match_channels: ['#mychannel']
match_event: 'permit'
```
diff --git a/docs/content/configuration/script-action.md b/docs/content/configuration/script-action.md
new file mode 100644
index 0000000..5fa3959
--- /dev/null
+++ b/docs/content/configuration/script-action.md
@@ -0,0 +1,51 @@
+---
+title: "Script Actions"
+---
+
+{{< lead >}}
+In order to maximize the flexibility of the bot you can trigger external scripts / commands in rules. These scripts are provided with extensive data to act on.
+{{< /lead >}}
+
+Your command will get a JSON object passed through `stdin` you can parse to gain details about the message. It is expected to yield an array of actions on `stdout` and exit with status `0`. If it does not the action will be marked failed. In case you need to output debug output you can use `stderr` which is directly piped to the bots `stderr`.
+
+This is an example input you might get on `stdin`:
+
+```json
+{
+ "badges": {
+ "glhf-pledge": 1,
+ "moderator": 1
+ },
+ "channel": "#tezrian",
+ "message": "!test",
+ "tags": {
+ "badge-info": "",
+ "badges": "moderator/1,glhf-pledge/1",
+ "client-nonce": "6801c82a341f728dbbaad87ef30eae49",
+ "color": "#A72920",
+ "display-name": "Luziferus",
+ "emotes": "",
+ "flags": "",
+ "id": "dca06466-3741-4b22-8339-4cb5b07a02cc",
+ "mod": "1",
+ "room-id": "485884564",
+ "subscriber": "0",
+ "tmi-sent-ts": "1610313040489",
+ "turbo": "0",
+ "user-id": "69699328",
+ "user-type": "mod"
+ },
+ "username": "luziferus"
+}
+```
+
+The example was dumped using this action:
+
+```yaml
+ - actions:
+ - type: script
+ attributes:
+ command: [/usr/bin/bash, -c, "jq . >&2"]
+ match_channels: ['#tezrian']
+ match_message: '^!test'
+```
diff --git a/wiki/Templating.md b/docs/content/configuration/templating.md
similarity index 93%
rename from wiki/Templating.md
rename to docs/content/configuration/templating.md
index af27010..97992e1 100644
--- a/wiki/Templating.md
+++ b/docs/content/configuration/templating.md
@@ -1,8 +1,12 @@
-## Templating
+---
+title: "Templating"
+---
+{{< lead >}}
Generally speaking the templating uses [Golang `text/template`](https://pkg.go.dev/text/template) template syntax. All fields with templating enabled do support the full synax from the `text/template` package.
+{{< /lead >}}
-### Variables
+## Variables
There are certain variables available in the strings with templating enabled:
@@ -12,7 +16,7 @@ There are certain variables available in the strings with templating enabled:
- `username` - The username of the message author
-### Functions
+## Functions
Within templates following functions can be used:
@@ -29,7 +33,7 @@ Examples below are using this syntax in the code block:
< Output from the template
```
-#### `arg`
+### `arg`
Takes the message sent to the channel, splits by space and returns the Nth element
@@ -43,7 +47,7 @@ Example:
< @tester please refrain from BSG
```
-#### `botHasBadge`
+### `botHasBadge`
Checks whether bot has the given badge in the current channel
@@ -56,7 +60,7 @@ Example:
< true
```
-#### `channelCounter`
+### `channelCounter`
Wraps the counter name into a channel specific counter name including the channel name
@@ -69,7 +73,7 @@ Example:
< 5
```
-#### `counterValue`
+### `counterValue`
Returns the current value of the counter which identifier was supplied
@@ -82,7 +86,7 @@ Example:
< 5
```
-#### `counterValueAdd`
+### `counterValueAdd`
Adds the given value (or 1 if no value) to the counter and returns its new value
@@ -94,7 +98,7 @@ Example:
< 1 6
```
-#### `displayName`
+### `displayName`
Returns the display name the specified user set for themselves
@@ -107,7 +111,7 @@ Example:
< Luziferus - foobar
```
-#### `doesFollow`
+### `doesFollow`
Returns whether `from` follows `to`
@@ -120,7 +124,7 @@ Example:
< true
```
-#### `doesFollowLongerThan`
+### `doesFollowLongerThan`
Returns whether `from` follows `to` for more than `duration`
@@ -133,7 +137,7 @@ Example:
< true
```
-#### `fixUsername`
+### `fixUsername`
Ensures the username no longer contains the `@` or `#` prefix
@@ -146,7 +150,7 @@ Example:
< luziferus - luziferus
```
-#### `formatDuration`
+### `formatDuration`
Returns a formated duration. Pass empty strings to leave out the specific duration part.
@@ -159,7 +163,7 @@ Example:
< 5 hours, 33 minutes, 12 seconds - 5 hours, 33 minutes
```
-#### `followAge`
+### `followAge`
Looks up when `from` followed `to` and returns the duration between then and now
@@ -172,7 +176,7 @@ Example:
< 15004h14m59.116620989s
```
-#### `followDate`
+### `followDate`
Looks up when `from` followed `to`
@@ -185,7 +189,7 @@ Example:
< 2021-04-10 16:07:07 +0000 UTC
```
-#### `group`
+### `group`
Gets matching group specified by index from `match_message` regular expression, when `fallback` is defined, it is used when group has an empty match
@@ -200,7 +204,7 @@ Example:
< test - oops
```
-#### `inList`
+### `inList`
Tests whether a string is in a given list of strings (for conditional templates).
@@ -215,7 +219,7 @@ Example:
< true
```
-#### `jsonAPI`
+### `jsonAPI`
Fetches remote URL and applies jq-like query to it returning the result as string. (Remote API needs to return status 200 within 5 seconds.)
@@ -230,7 +234,7 @@ Example:
< example string
```
-#### `lastPoll`
+### `lastPoll`
Gets the last (currently running or archived) poll for the given channel (the channel must have given extended permission for poll access!)
@@ -245,7 +249,7 @@ Example:
See schema of returned object in [`pkg/twitch/polls.go#L13`](https://github.com/Luzifer/twitch-bot/blob/master/pkg/twitch/polls.go#L13)
-#### `lastQuoteIndex`
+### `lastQuoteIndex`
Gets the last quote index in the quote database for the current channel
@@ -258,7 +262,7 @@ Example:
< Last Quote: #32
```
-#### `mention`
+### `mention`
Strips username and converts into a mention
@@ -271,7 +275,7 @@ Example:
< @user @user @user
```
-#### `pow`
+### `pow`
Returns float from calculation: `float1 ** float2`
@@ -284,7 +288,7 @@ Example:
< 10000
```
-#### `randomString`
+### `randomString`
Randomly picks a string from a list of strings
@@ -297,7 +301,7 @@ Example:
< a
```
-#### `recentGame`
+### `recentGame`
Returns the last played game name of the specified user (see shoutout example) or the `fallback` if the game could not be fetched. If no fallback was supplied the message will fail and not be sent.
@@ -311,7 +315,7 @@ Example:
```
-#### `recentTitle`
+### `recentTitle`
Returns the last stream title of the specified user or the `fallback` if the title could not be fetched. If no fallback was supplied the message will fail and not be sent.
@@ -324,7 +328,7 @@ Example:
< Die Oper haben wir überlebt, mal sehen was uns sonst noch alles töten möchte… - none
```
-#### `seededRandom`
+### `seededRandom`
Returns a float value stable for the given seed
@@ -337,7 +341,7 @@ Example:
< Your int this hour: 17%
```
-#### `streamUptime`
+### `streamUptime`
Returns the duration the stream is online (causes an error if no current stream is found)
@@ -350,7 +354,7 @@ Example:
< 3 hours, 56 minutes
```
-#### `subCount`
+### `subCount`
Returns the number of subscribers (accounts) currently subscribed to the given channel
@@ -363,7 +367,7 @@ Example:
< 26
```
-#### `subPoints`
+### `subPoints`
Returns the number of sub-points currently given through the T1 / T2 / T3 subscriptions to the given channel
@@ -376,7 +380,7 @@ Example:
< 26
```
-#### `tag`
+### `tag`
Takes the message sent to the channel, returns the value of the tag specified
@@ -389,7 +393,7 @@ Example:
< luziferus
```
-#### `textAPI`
+### `textAPI`
Fetches remote URL and returns the result as string. (Remote API needs to return status 200 within 5 seconds.)
@@ -404,7 +408,7 @@ Example:
< Weather for Hamburg, DE: Few clouds with a temperature of 22 C (71.6 F). [...]
```
-#### `variable`
+### `variable`
Returns the variable value or default in case it is empty
@@ -417,7 +421,7 @@ Example:
< test - fallback
```
-### Upgrade from `v2.x` to `v3.x`
+## Upgrade from `v2.x` to `v3.x`
When adding [sprig](https://masterminds.github.io/sprig/) function collection some functions collided and needed replacement. You need to adapt your templates accordingly:
diff --git a/docs/content/getting-started/_index.md b/docs/content/getting-started/_index.md
new file mode 100644
index 0000000..0ab451d
--- /dev/null
+++ b/docs/content/getting-started/_index.md
@@ -0,0 +1,14 @@
+---
+title: "Getting started"
+weight: 1
+---
+
+{{< lead >}}
+Get started with the installation of the Twitch-Bot and get to the point you do have a working bot inside your channel.
+{{< /lead >}}
+
+The majority of this sections assumes you are working on a **Linux Server** (systemd based, like Debian / Ubuntu) with direct internet access. If you are doing your setup on another system you might need to adapt steps or even replace them. The basic setup should be the same but some steps will differ.
+
+If you don't have a server yet you could get a [Hetzner Cloud server (referral link)](https://hetzner.cloud/?ref=843sgmhTGlwR) for little more than 5 Euro per month which is perfectly capable to drive your bot!
+
+Installing the bot on your **local computer** is not advisable as your bot will miss events occurring when you're offline / your computer is turned off. Also it will suffer from instabilities in your local internet connection as datacenter-connections are typically way more stable than your local domestical internet connection. (Though it is possible to use the bot on your local computer, I'd just advice against it.)
diff --git a/docs/content/getting-started/configuration.md b/docs/content/getting-started/configuration.md
new file mode 100644
index 0000000..b7b44a0
--- /dev/null
+++ b/docs/content/getting-started/configuration.md
@@ -0,0 +1,27 @@
+---
+title: "Configuration"
+weight: 5
+---
+
+{{< lead >}}
+After you finally can access your bot through the [External Access]({{< ref "external-access.md" >}}) you can start to configure it!
+{{< /lead >}}
+
+In order to gain access to the bot you need to add yourself as an editor to the configuration. To do so edit the configuration created during the [Service Setup]({{< ref "setup.md" >}}) (in our example `/var/lib/twitch-bot/config.yaml`) and modify the bot editors:
+
+```yaml
+# List of strings: Either Twitch user-ids or nicknames (best to stick
+# with IDs as they can't change while nicknames can be changed every
+# 60 days). Those users are able to use the config editor web-interface.
+bot_editors: [ 'luziferus' ]
+```
+
+Of course you should not enter my nickname but yours into the list. Don't worry about the hint IDs should be used. Using the nickname is fine for now and the bot will automatically adjust the entry when you start configuring the bot through the web-interface.
+
+The confiuration change is automatically loaded by the bot (you should see that in the service log) and you should now be able to log into the web-interface using the **Login with Twitch** button.
+
+At this point you've fully set up the bot to run and you've gained access to it and with this state the "Getting Started" guide ends. You can now
+
+- add a channel in the web-interface, configure rules with [actions]({{< ref "../configuration/actors.md" >}}) and [templates]({{< ref "../configuration/templating.md" >}})
+- have a look on all the other options in the [Config-File Syntax]({{< ref "../configuration/config-file.md" >}})
+- discover what the bot can do in the [Rule Examples]({{< ref "../configuration/rule-examples.md" >}})
diff --git a/docs/content/getting-started/external-access.md b/docs/content/getting-started/external-access.md
new file mode 100644
index 0000000..1bcf88e
--- /dev/null
+++ b/docs/content/getting-started/external-access.md
@@ -0,0 +1,56 @@
+---
+title: "External Access"
+weight: 4
+---
+
+{{< lead >}}
+In order to be able to access the bot through a web-browser and make configuration changes using the web-interface we need to securely expose the interface to the internet.
+{{< /lead >}}
+
+{{< alert style="info" >}}
+In case you did the installation of the bot on your **local machine**, skip this part. You can access the web-interface at `http://localhost:3000/` after you've continued with the [Configuration]({{< ref "configuration.md" >}}).
+{{< /alert >}}
+
+In order not to make this a quite long and extensive tutorial we'll use two tutorials of DigitalOcean to aid us:
+
+- [DigitalOcean: How To Install Nginx on Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04)
+- [DigitalOcean: How To Secure Nginx with Let's Encrypt on Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04)
+
+We will follow the first one up to step 4, and omit step 5 and 6. These are not required for us as they are configuring a locally hosted website which we don't want to do for the bot. Instead of the suggested `/etc/nginx/sites-available/your_domain` file in the first tutorial we will create a `/etc/nginx/sites-available/twitch-bot.conf` file with the following content:
+
+```nginx
+server {
+ listen 80;
+ listen [::]:80;
+
+ server_name twitch-bot.mydomain.com;
+
+ location / {
+ add_header X-Robots-Tag noindex;
+
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade";
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ proxy_pass http://127.0.0.1:3000/;
+ }
+}
+```
+
+Make sure to replace the `server_name` directive with the (sub-)domain you want the bot to be available at.
+
+After creating the file we'll enable it similar to the tutorial:
+
+```console
+# Link the configuration to the enabled ones
+$ sudo ln -s /etc/nginx/sites-available/twitch-bot.conf /etc/nginx/sites-enabled/
+
+# Check whether there are any errors
+$ sudo nginx -t
+
+# If there were no errors, restart the nginx service
+$ sudo systemctl restart nginx
+```
diff --git a/docs/content/getting-started/installation.md b/docs/content/getting-started/installation.md
new file mode 100644
index 0000000..a62572f
--- /dev/null
+++ b/docs/content/getting-started/installation.md
@@ -0,0 +1,50 @@
+---
+title: "Installation"
+weight: 1
+---
+
+{{< lead >}}
+Installation is the first step you need to take, therefore choose how you want to proceed:
+{{< /lead >}}
+
+## Downloading a pre-compiled Binary
+
+You can always find the latest pre-compiled binary on the [Releases-Page](https://github.com/Luzifer/twitch-bot/releases) of the Github repository. The latest release contains binaries for MacOS (`darwin`), Linux (`amd64` / `arm`) and Windows.
+
+This way of installation is the easiest one:
+
+- Download the archive for your system
+- Unzip / untar the archive
+- Find a binary in the location you unpacked the archive to
+
+The binary you get from this is everything you need: It contains the bot as well as the web-interface to configure the bot.
+
+Move this binary to a location you will find it again later.
+
+## Using a pre-built Docker image
+
+The Docker image is automatically built from the source and provided in two different variants:
+
+| Image | Description |
+| ----- | ----------- |
+| `luzifer/twitch-bot:latest` | The latest development version, not recommended for use as your main bot, perfect for testing the latest changes not yet released. |
+| `luzifer/twitch-bot:stable` | Automatically updated on every versioned release, you just can use this tag to always have the latest stable version. Pay attention: This automatically switches over on major / breaking releases! |
+| `luzifer/twitch-bot:
EventClient abstracts the connection to the bot websocket for events
+Object
Options to pass to the EventClient constructor
+string
+ * [.paramOptionFallback(key, [fallback])](#EventClient+paramOptionFallback) ⇒ \*
+ * [.renderTemplate(template)](#EventClient+renderTemplate) ⇒ Promise
+
+
+
+### new EventClient(opts)
+Creates, initializes and connects the EventClient
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| opts | [Options
](#Options) | Options for the EventClient |
+
+
+
+### eventClient.apiBase() ⇒ string
+Returns the API base URL without trailing slash
+
+**Kind**: instance method of [EventClient
](#EventClient)
+**Returns**: string
- API base URL
+
+
+### eventClient.paramOptionFallback(key, [fallback]) ⇒ \*
+Resolves the given key through url hash parameters with fallback to constructor options
+
+**Kind**: instance method of [EventClient
](#EventClient)
+**Returns**: \*
- Value of the key or `null`
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| key | string
| | The key to resolve |
+| [fallback] | \*
|
| Fallback to return if neither params nor options contained that key |
+
+
+
+### eventClient.renderTemplate(template) ⇒ Promise
+Renders a given template using the bots msgformat API (supports all templating you can use in bot messages). To use this function the token passed through the constructor or the URL hash must have the `msgformat` permission in addition to the `overlays` permission.
+
+**Kind**: instance method of [EventClient
](#EventClient)
+**Returns**: Promise
- Promise resolving to the rendered output of the template
+
+| Param | Type | Description |
+| --- | --- | --- |
+| template | string
| The template to render |
+
+
+
+## Options : Object
+Options to pass to the EventClient constructor
+
+**Kind**: global typedef
+**Properties**
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| [channel] | string
| | Filter for specific channel events (format: `#channel`) |
+| [handlers] | Object
| {}
| Map event types to callback functions `(event, fields, time, live) => {...}` |
+| [maxReplayAge] | number
| -1
| Number of hours to replay the events for (-1 = infinite) |
+| [replay] | boolean
| false
| Request a replay at connect (requires channel to be set to a channel name) |
+| [token] | string
| | API access token to use to connect to the WebSocket (if not set, must be provided through URL hash) |
+
diff --git a/docs/content/overlays/soundalerts.md b/docs/content/overlays/soundalerts.md
new file mode 100644
index 0000000..f9d341a
--- /dev/null
+++ b/docs/content/overlays/soundalerts.md
@@ -0,0 +1,105 @@
+---
+title: Sound-Alerts
+weight: 50
+---
+
+{{< lead >}}
+The Sound-Alerts overlay provides you with a way to let your viewers trigger sounds through channel-points and chat commands.
+{{< /lead >}}
+
+This overlay utilizes the custom events actor to generate events specific to the overlay in order to trigger sound alerts.
+
+## OBS Setup
+
+To use it
+
+- generate an API token with the `overlays` permission and note it in a secure place
+- add a Browser-Source to your OBS scenes with at least 1×1 px in size
+- set the URL to `https://your-bot.example.com/overlays/sounds.html?token=[your-token]&channel=[your-channel]`
+
+After you've done this you're already done with the setup inside your OBS.
+
+## Add the sound-files to the overlays directory
+
+In order to be able to play those sounds the sounds need to be added to overlays directory. You cannot use remote sounds hosted somewhere else because of security limitations.
+
+The Linux OBS browser-source does not allow for proprietary codecs to be used so the sounds should be converted to sound files containing the Opus Codec. (Though this isn't strictly required for Windows OBS it might be a good idea to use this codec in general as it is a non-proprietary codec.)
+
+If you got a sound file in `mp3`, `wav`, `aac` or whatever format you can use [`ffmpeg`](https://ffmpeg.org/) to convert it to a `webm` file which automatically will use the Opus codec:
+
+```console
+ffmpeg mysoundfile.mp3 mysoundfile.webm
+```
+
+In the following example rules I'll assume you created a `sounds` directory inside your overlays directory and stored some sound files in there.
+
+## Creating Rules to trigger Sounds
+
+### Using Channel-Points
+
+```yaml
+- description: Channelpoint SoundAlert
+ actions:
+ - type: customevent
+ attributes:
+ fields: |
+ {{
+ $sound := get ( dict
+ "b8afa5fa-2441-43ef-a634-77d0ad4f2691" "applaus"
+ "63c8d002-89f4-41fc-b6c9-03b339f439cd" "badum"
+ "39c34f65-85e3-4792-9e16-ff3d186aaf6f" "bonk"
+ "b9f41ea4-3fcb-41d8-9072-f0923089d097" "tick"
+ "f67c792e-2555-486e-b441-8247cb8ac97e" "typewriter"
+ "c9ca69a6-9d2e-4b39-90c9-26a4b62064fc" "zirp"
+ ) .reward_id
+ }}
+ {
+ "type": "soundalert",
+ "soundUrl": "sounds/{{ $sound }}.webm"
+ }
+ match_channels:
+ - '#luziferus'
+ match_event: channelpoint_redeem
+ disable_on_offline: true
+ disable_on_template: '{{ not (hasPrefix "Sound: " .reward_title) }}'
+```
+
+On the first glimps this seems rather complicated so lets look at it step-by-step:
+
+- We defined a new rule for our Channel-Points Sound-Alerts
+- This rule reacts on redeemed Channel-Points: `match_event: channelpoint_redeem`
+- It does not trigger when the channel is offline and it does not trigger when the Channel-Points reward is not named `Sound: [...]`
+- It only triggers in my channel: `match_channels: ['#luziferus']`
+- When all the conditions mentioned above are met the rule will create a `customevent` containing the `"type": "soundalert"` which is used by the overlay we've set up in OBS before
+- It gives the `"soundUrl": "sounds/{{ $sound }}.webm"`, therefore telling the overlay to play the file `sounds/{{ $sound }}.webm` when the event is received
+- Now to the "complicated" part: In order not to define a rule for each single sound I've fused all the rewards together into one rule with a mapping of the `.reward_id` to the name of the sound file to be played. This is done through creating a `dict` with the reward id and the name of the file and a `get` function around it selecting the `.reward_id` from the given `dict`.
+
+Before adding this to your bot create at least one **Custom Reward** in the channel-points section in your streamer dashboard. The **Reward Name** should be `Sound: [...]` in order to be matched by the rule above. The **Reward Description** is optional, the viewer should not enter a text, the **Cost** is something you can freely define (I'm using 250 points), the **Reward Icon** too. I'd advice to enable **Skip Reward Requests Queue** as the bot will automatically trigger and disabling this would clutter your Reward Requests Queue. Also you might want to enable the **Cooldown**: I'm using a 5min cooldown on each sound for them not to be spammed permanently.
+
+You now can simply copy the rule into your bot configuration and just adjust the channel, the reward-ids and the sound file names. To get the `reward_id` of the event have a look into the Debug-Overlay described on the [Overlays]({{< ref "_index.md" >}}) page and trigger the reward once. (If you're doing this being offline just disable the cooldown for a moment to be able to trigger the reward while being offline.)
+
+### Using Chat-Commands
+
+```yaml
+- description: Command SoundAlert
+ actions:
+ - type: customevent
+ attributes:
+ fields: |
+ {
+ "type": "soundalert",
+ "soundUrl": "sounds/{{ group 1 }}.webm"
+ }
+ cooldown: 5m
+ match_channels:
+ - '#luziferus'
+ match_message: '(?i)^!sound (applaus|badum|bonk|tick)$'
+ disable_on_offline: true
+```
+
+As we don't have to deal with the Twitch reward-id stuff and just define a command this rule is rather simple but lets have a quick walkthrough too:
+
+- We defined a rule for the `!sound` command: `match_message: '(?i)^!sound (applaus|badum|bonk|tick)$'`
+- To add a little bit of security here the command also defines which sounds can be triggered: `(applaus|badum|bonk|tick)`
+- Also this rule does not trigger when the channel is offline and defines a 5 minute cooldown (this is not per sound but for all sounds so triggering `!sound applaus` will set the cooldown also for `!sound tick`)
+- Finally the rule also creates the `customevent` of `"type": "soundalert"` passing a `soundUrl` where to find the sound
diff --git a/docs/hugo.toml b/docs/hugo.toml
new file mode 100644
index 0000000..12423f5
--- /dev/null
+++ b/docs/hugo.toml
@@ -0,0 +1,35 @@
+baseURL = '/'
+
+languageCode = 'en-us'
+pygmentsCodeFences = true
+pygmentsStyle = "monokailight"
+theme = "ace-documentation"
+title = 'Twitch-Bot Documentation'
+
+defaultContentLanguage = "en"
+defaultContentLanguageInSubdir= false
+enableMissingTranslationPlaceholders = false
+
+cleanDestinationDir = true
+disableKinds = ["RSS"]
+publishDir = "../.rendered-docs"
+
+[params]
+disableReadmoreNav = true # set true to hide prev/next navigation, default is false
+disableSearch = false # default is false
+highlightClientSide = false # set true to use highlight.pack.js instead of the default hugo chroma highlighter
+menushortcutsnewtab = true # set true to open shortcuts links to a new tab/window
+ordersectionsby = "weight" # ordersectionsby = "title"
+
+[markup]
+ [markup.goldmark]
+ [markup.goldmark.renderer]
+ unsafe = true
+
+[outputs]
+home = [ "HTML", "JSON"]
+
+[[menu.shortcuts]]
+name = ""
+url = "https://github.com/Luzifer/twitch-bot"
+weight = 2
diff --git a/docs/static/screen-twitch-console-register-app.png b/docs/static/screen-twitch-console-register-app.png
new file mode 100644
index 0000000..fe93ac0
--- /dev/null
+++ b/docs/static/screen-twitch-console-register-app.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6ba856c767a8fe0de2520eb6a64cddfb7b01ecb8a7812524a85626e388dbf634
+size 62344
diff --git a/docs/themes/ace-documentation b/docs/themes/ace-documentation
new file mode 160000
index 0000000..2415924
--- /dev/null
+++ b/docs/themes/ace-documentation
@@ -0,0 +1 @@
+Subproject commit 241592496471791045d36500cd9add18caa6ae43
diff --git a/internal/apimodules/overlays/default/debug.html b/internal/apimodules/overlays/default/debug.html
index e1524c7..6263018 100644
--- a/internal/apimodules/overlays/default/debug.html
+++ b/internal/apimodules/overlays/default/debug.html
@@ -46,7 +46,7 @@