mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 11:51:17 +00:00
[templating] Add multiply
and seededRandom
template functions
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
c6fa5a9df9
commit
c884a7c532
5 changed files with 326 additions and 31 deletions
10
internal/template/numeric/numeric.go
Normal file
10
internal/template/numeric/numeric.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package numeric
|
||||
|
||||
import "github.com/Luzifer/twitch-bot/plugins"
|
||||
|
||||
func Register(args plugins.RegistrationArguments) error {
|
||||
args.RegisterTemplateFunction("multiply", plugins.GenericTemplateFunctionGetter(multiply))
|
||||
return nil
|
||||
}
|
||||
|
||||
func multiply(m1, m2 float64) float64 { return m1 * m2 }
|
43
internal/template/random/random.go
Normal file
43
internal/template/random/random.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"crypto/md5" // #nosec G501 // Unly used to convert a string into a numer, no need for cryptographic safety
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
|
||||
"github.com/Luzifer/twitch-bot/plugins"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func Register(args plugins.RegistrationArguments) error {
|
||||
args.RegisterTemplateFunction("seededRandom", plugins.GenericTemplateFunctionGetter(stableRandomFromSeed))
|
||||
return nil
|
||||
}
|
||||
|
||||
func stableRandomFromSeed(seed string) (float64, error) {
|
||||
seedValue, err := stringToSeed(seed)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "generating seed")
|
||||
}
|
||||
|
||||
return rand.New(rand.NewSource(seedValue)).Float64(), nil // #nosec G404 // Only used for generating a random number from static string, no need for cryptographic safety
|
||||
}
|
||||
|
||||
func stringToSeed(s string) (int64, error) {
|
||||
hash := md5.New() // #nosec G401 // Unly used to convert a string into a numer, no need for cryptographic safety
|
||||
if _, err := fmt.Fprint(hash, s); err != nil {
|
||||
return 0, errors.Wrap(err, "writing string to hasher")
|
||||
}
|
||||
|
||||
var (
|
||||
hashSum = hash.Sum(nil)
|
||||
sum int64
|
||||
)
|
||||
|
||||
for i := 0; i < len(hashSum); i++ {
|
||||
sum += int64(float64(hashSum[len(hashSum)-1-i]%10) * math.Pow(10, float64(i))) //nolint:gomnd // No need to put the 10 of 10**i into a constant named "ten"
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
|
@ -16,6 +16,8 @@ import (
|
|||
"github.com/Luzifer/twitch-bot/internal/actors/respond"
|
||||
"github.com/Luzifer/twitch-bot/internal/actors/timeout"
|
||||
"github.com/Luzifer/twitch-bot/internal/actors/whisper"
|
||||
"github.com/Luzifer/twitch-bot/internal/template/numeric"
|
||||
"github.com/Luzifer/twitch-bot/internal/template/random"
|
||||
"github.com/Luzifer/twitch-bot/plugins"
|
||||
"github.com/Luzifer/twitch-bot/twitch"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -23,7 +25,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
coreActorRegistations = []plugins.RegisterFunc{
|
||||
corePluginRegistrations = []plugins.RegisterFunc{
|
||||
// Actors
|
||||
ban.Register,
|
||||
delay.Register,
|
||||
deleteactor.Register,
|
||||
|
@ -35,13 +38,17 @@ var (
|
|||
respond.Register,
|
||||
timeout.Register,
|
||||
whisper.Register,
|
||||
|
||||
// Template functions
|
||||
numeric.Register,
|
||||
random.Register,
|
||||
}
|
||||
knownModules []string
|
||||
)
|
||||
|
||||
func initCorePlugins() error {
|
||||
args := getRegistrationArguments()
|
||||
for _, rf := range coreActorRegistations {
|
||||
for _, rf := range corePluginRegistrations {
|
||||
if err := rf(args); err != nil {
|
||||
return errors.Wrap(err, "registering core plugin")
|
||||
}
|
29
wiki/Home.md
29
wiki/Home.md
|
@ -140,35 +140,6 @@ rules: # See below for examples
|
|||
...
|
||||
```
|
||||
|
||||
## Templating
|
||||
|
||||
There are certain variables available in the strings with templating enabled:
|
||||
|
||||
- `channel` - Channel the message was sent to, only available for regular messages not events
|
||||
- `msg` - The message object, used in functions, should not be sent to chat
|
||||
- `permitTimeout` - Value of `permit_timeout` in seconds
|
||||
- `username` - The username of the message author
|
||||
|
||||
Additionally there are some functions available in the templates:
|
||||
|
||||
- `arg <idx>` - Takes the message sent to the channel, splits by space and returns the Nth element
|
||||
- `botHasBadge <badge>` - Checks whether bot has the given badge in the current channel
|
||||
- `channelCounter <counter name>` - Wraps the counter name into a channel specific counter name including the channel name
|
||||
- `concat <delimiter> <...parts>` - Join the given string parts with delimiter
|
||||
- `counterValue <counter name>` - Returns the current value of the counter which identifier was supplied
|
||||
- `displayName <username> [fallback]` - Returns the display name the specified user set for themselves
|
||||
- `fixUsername <username>` - Ensures the username no longer contains the `@` or `#` prefix
|
||||
- `formatDuration <duration> <hours> <minutes> <seconds>` - Returns a formated duration. Pass empty strings to leave out the part: `{{ formatDuration .dur "hours" "minutes" "" }}` yields `N hours, M minutes`
|
||||
- `followDate <from> <to>` - Looks up when `from` followed `to`
|
||||
- `group <idx> [fallback]` - Gets matching group specified by index from `match_message` regular expression, when `fallback` is defined, it is used when group has an empty match
|
||||
- `lastQuoteIndex` - Gets the last quote index in the quote database for the current channel
|
||||
- `recentGame <username> [fallback]` - 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.
|
||||
- `streamUptime <username>` - Returns the duration the stream is online (causes an error if no current stream is found)
|
||||
- `tag <tagname>` - Takes the message sent to the channel, returns the value of the tag specified
|
||||
- `toLower <string>` - Converts the given string to lower-case
|
||||
- `toUpper <string>` - Converts the given string to upper-case
|
||||
- `variable <name> [default]` - Returns the variable value or default in case it is empty
|
||||
|
||||
## 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`.
|
||||
|
|
264
wiki/Templating.md
Normal file
264
wiki/Templating.md
Normal file
|
@ -0,0 +1,264 @@
|
|||
## Templating
|
||||
|
||||
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.
|
||||
|
||||
### Variables
|
||||
|
||||
There are certain variables available in the strings with templating enabled:
|
||||
|
||||
- `channel` - Channel the message was sent to, only available for regular messages not events
|
||||
- `msg` - The message object, used in functions, should not be sent to chat
|
||||
- `permitTimeout` - Value of `permit_timeout` in seconds
|
||||
- `username` - The username of the message author
|
||||
|
||||
|
||||
### Functions
|
||||
|
||||
Additionally to the built-in functions there are extra functions available in the templates:
|
||||
|
||||
Examples below are using this syntax in the code block:
|
||||
|
||||
```
|
||||
! Message matcher used for the input message
|
||||
> Input message if used in the example
|
||||
# Template used in the fields
|
||||
< Output from the template
|
||||
```
|
||||
|
||||
#### `arg`
|
||||
|
||||
Takes the message sent to the channel, splits by space and returns the Nth element
|
||||
|
||||
Syntax: `arg <index>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
> !bsg @tester
|
||||
# {{ arg 1 }} please refrain from BSG
|
||||
< @tester please refrain from BSG
|
||||
```
|
||||
|
||||
#### `botHasBadge`
|
||||
|
||||
Checks whether bot has the given badge in the current channel
|
||||
|
||||
Syntax: `botHasBadge <badge>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ botHasBadge "moderator" }}
|
||||
< true
|
||||
```
|
||||
|
||||
#### `channelCounter`
|
||||
|
||||
Wraps the counter name into a channel specific counter name including the channel name
|
||||
|
||||
Syntax: `channelCounter <counter name>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ channelCounter "test" }}
|
||||
< 5
|
||||
```
|
||||
|
||||
#### `concat`
|
||||
|
||||
Join the given string parts with delimiter
|
||||
|
||||
Syntax: `concat <delimiter> <...parts>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ concat ":" "test" .username }}
|
||||
< test:luziferus
|
||||
```
|
||||
|
||||
#### `counterValue`
|
||||
|
||||
Returns the current value of the counter which identifier was supplied
|
||||
|
||||
Syntax: `counterValue <counter name>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ counterValue (concat ":" .channel "test") }}
|
||||
< 5
|
||||
```
|
||||
|
||||
#### `displayName`
|
||||
|
||||
Returns the display name the specified user set for themselves
|
||||
|
||||
Syntax: `displayName <username> [fallback]`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ displayName "luziferus" }} - {{ displayName "notexistinguser" "foobar" }}
|
||||
< Luziferus - foobar
|
||||
```
|
||||
|
||||
#### `fixUsername`
|
||||
|
||||
Ensures the username no longer contains the `@` or `#` prefix
|
||||
|
||||
Syntax: `fixUsername <username>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ fixUsername .channel }} - {{ fixUsername "@luziferus" }}
|
||||
< luziferus - luziferus
|
||||
```
|
||||
|
||||
#### `formatDuration`
|
||||
|
||||
Returns a formated duration. Pass empty strings to leave out the specific duration part.
|
||||
|
||||
Syntax: `formatDuration <duration> <hours> <minutes> <seconds>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ formatDuration (streamUptime .channel) "hours" "minutes" "seconds" }} - {{ formatDuration (streamUptime .channel) "hours" "minutes" "" }}
|
||||
< 5 hours, 33 minutes, 12 seconds - 5 hours, 33 minutes
|
||||
```
|
||||
|
||||
#### `followDate`
|
||||
|
||||
Looks up when `from` followed `to`
|
||||
|
||||
Syntax: `followDate <from> <to>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ followDate "tezrian" "luziferus" }}
|
||||
< 2021-04-10 16:07:07 +0000 UTC
|
||||
```
|
||||
|
||||
#### `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
|
||||
|
||||
Syntax: `group <idx> [fallback]`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
! !command ([0-9]+) ([a-z]+) ([a-z]*)
|
||||
> !command 12 test
|
||||
# {{ group 2 "oops" }} - {{ group 3 "oops" }}
|
||||
< test - oops
|
||||
```
|
||||
|
||||
#### `lastQuoteIndex`
|
||||
|
||||
Gets the last quote index in the quote database for the current channel
|
||||
|
||||
Syntax: `lastQuoteIndex`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# Last Quote: #{{ lastQuoteIndex }}
|
||||
< Last Quote: #32
|
||||
```
|
||||
|
||||
#### `multiply`
|
||||
|
||||
Returns float from calculation: `float1 * float2`
|
||||
|
||||
Syntax: `multiply <float1> <float2>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ printf "%.0f" (multiply 100 (seededRandom "test")) }}%
|
||||
< 35%
|
||||
```
|
||||
|
||||
#### `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.
|
||||
|
||||
Syntax: `recentGame <username> [fallback]`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ recentGame "luziferus" "none" }} - {{ recentGame "thisuserdoesnotexist123" "none" }}
|
||||
< Metro Exodus - none
|
||||
```
|
||||
|
||||
#### `streamUptime`
|
||||
|
||||
Returns the duration the stream is online (causes an error if no current stream is found)
|
||||
|
||||
Syntax: `streamUptime <username>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ formatDuration (streamUptime "luziferus") "hours" "minutes" "" }}
|
||||
< 3 hours, 56 minutes
|
||||
```
|
||||
|
||||
#### `seededRandom`
|
||||
|
||||
Returns a float value stable for the given seed
|
||||
|
||||
Syntax: `seededRandom <string-seed>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# Your int this hour: {{ printf "%.0f" (multiply (seededRandom (concat ":" "int" .username (now "2006-01-02 15"))) 100) }}%
|
||||
< Your int this hour: 17%
|
||||
```
|
||||
|
||||
#### `tag`
|
||||
|
||||
Takes the message sent to the channel, returns the value of the tag specified
|
||||
|
||||
Syntax: `tag <tagname>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ tag "login" }}
|
||||
< luziferus
|
||||
```
|
||||
|
||||
#### `toLower` / `toUpper`
|
||||
|
||||
Converts the given string to lower-case / upper-case
|
||||
|
||||
Syntax: `toLower <string>` / `toUpper <string>`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ toLower "Test" }} - {{ toUpper "Test" }}
|
||||
< test - TEST
|
||||
```
|
||||
|
||||
#### `variable`
|
||||
|
||||
Returns the variable value or default in case it is empty
|
||||
|
||||
Syntax: `variable <name> [default]`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# {{ variable "foo" "fallback" }} - {{ variable "unsetvar" "fallback" }}
|
||||
< test - fallback
|
||||
```
|
||||
|
Loading…
Reference in a new issue