mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 20:01:17 +00:00
Add support to disable cooldown through the action module
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
0dc19d8eed
commit
2d4efb4832
11 changed files with 69 additions and 59 deletions
|
@ -15,12 +15,12 @@ type ActorBan struct {
|
||||||
Ban *string `json:"ban" yaml:"ban"`
|
Ban *string `json:"ban" yaml:"ban"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorBan) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorBan) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if a.Ban == nil {
|
if a.Ban == nil {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
c.WriteMessage(&irc.Message{
|
c.WriteMessage(&irc.Message{
|
||||||
Command: "PRIVMSG",
|
Command: "PRIVMSG",
|
||||||
Params: []string{
|
Params: []string{
|
||||||
|
|
|
@ -17,28 +17,28 @@ type ActorCounter struct {
|
||||||
Counter *string `json:"counter" yaml:"counter"`
|
Counter *string `json:"counter" yaml:"counter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if a.Counter == nil {
|
if a.Counter == nil {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
counterName, err := formatMessage(*a.Counter, m, r, nil)
|
counterName, err := formatMessage(*a.Counter, m, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "preparing response")
|
return false, errors.Wrap(err, "preparing response")
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.CounterSet != nil {
|
if a.CounterSet != nil {
|
||||||
parseValue, err := formatMessage(*a.CounterSet, m, r, nil)
|
parseValue, err := formatMessage(*a.CounterSet, m, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "execute counter value template")
|
return false, errors.Wrap(err, "execute counter value template")
|
||||||
}
|
}
|
||||||
|
|
||||||
counterValue, err := strconv.ParseInt(parseValue, 10, 64) //nolint:gomnd // Those numbers are static enough
|
counterValue, err := strconv.ParseInt(parseValue, 10, 64) //nolint:gomnd // Those numbers are static enough
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "parse counter value")
|
return false, errors.Wrap(err, "parse counter value")
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
store.UpdateCounter(counterName, counterValue, true),
|
store.UpdateCounter(counterName, counterValue, true),
|
||||||
"set counter",
|
"set counter",
|
||||||
)
|
)
|
||||||
|
@ -49,7 +49,7 @@ func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
||||||
counterStep = *a.CounterStep
|
counterStep = *a.CounterStep
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
store.UpdateCounter(counterName, counterStep, false),
|
store.UpdateCounter(counterName, counterStep, false),
|
||||||
"update counter",
|
"update counter",
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,9 +16,9 @@ type ActorDelay struct {
|
||||||
DelayJitter time.Duration `json:"delay_jitter" yaml:"delay_jitter"`
|
DelayJitter time.Duration `json:"delay_jitter" yaml:"delay_jitter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorDelay) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorDelay) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if a.Delay == 0 && a.DelayJitter == 0 {
|
if a.Delay == 0 && a.DelayJitter == 0 {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
totalDelay := a.Delay
|
totalDelay := a.Delay
|
||||||
|
@ -27,7 +27,7 @@ func (a ActorDelay) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(totalDelay)
|
time.Sleep(totalDelay)
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorDelay) IsAsync() bool { return false }
|
func (a ActorDelay) IsAsync() bool { return false }
|
||||||
|
|
|
@ -15,17 +15,17 @@ type ActorDelete struct {
|
||||||
DeleteMessage *bool `json:"delete_message" yaml:"delete_message"`
|
DeleteMessage *bool `json:"delete_message" yaml:"delete_message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorDelete) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorDelete) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if a.DeleteMessage == nil || !*a.DeleteMessage {
|
if a.DeleteMessage == nil || !*a.DeleteMessage {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
msgID, ok := m.Tags.GetTag("id")
|
msgID, ok := m.Tags.GetTag("id")
|
||||||
if !ok || msgID == "" {
|
if !ok || msgID == "" {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
c.WriteMessage(&irc.Message{
|
c.WriteMessage(&irc.Message{
|
||||||
Command: "PRIVMSG",
|
Command: "PRIVMSG",
|
||||||
Params: []string{
|
Params: []string{
|
||||||
|
|
|
@ -13,22 +13,22 @@ type ActorRaw struct {
|
||||||
RawMessage *string `json:"raw_message" yaml:"raw_message"`
|
RawMessage *string `json:"raw_message" yaml:"raw_message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorRaw) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorRaw) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if a.RawMessage == nil {
|
if a.RawMessage == nil {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rawMsg, err := formatMessage(*a.RawMessage, m, r, nil)
|
rawMsg, err := formatMessage(*a.RawMessage, m, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "preparing raw message")
|
return false, errors.Wrap(err, "preparing raw message")
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := irc.ParseMessage(rawMsg)
|
msg, err := irc.ParseMessage(rawMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "parsing raw message")
|
return false, errors.Wrap(err, "parsing raw message")
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
c.WriteMessage(msg),
|
c.WriteMessage(msg),
|
||||||
"sending raw message",
|
"sending raw message",
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,18 +15,18 @@ type ActorRespond struct {
|
||||||
RespondFallback *string `json:"respond_fallback" yaml:"respond_fallback"`
|
RespondFallback *string `json:"respond_fallback" yaml:"respond_fallback"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorRespond) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorRespond) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if a.Respond == nil {
|
if a.Respond == nil {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := formatMessage(*a.Respond, m, r, nil)
|
msg, err := formatMessage(*a.Respond, m, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if a.RespondFallback == nil {
|
if a.RespondFallback == nil {
|
||||||
return errors.Wrap(err, "preparing response")
|
return false, errors.Wrap(err, "preparing response")
|
||||||
}
|
}
|
||||||
if msg, err = formatMessage(*a.RespondFallback, m, r, nil); err != nil {
|
if msg, err = formatMessage(*a.RespondFallback, m, r, nil); err != nil {
|
||||||
return errors.Wrap(err, "preparing response fallback")
|
return false, errors.Wrap(err, "preparing response fallback")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ func (a ActorRespond) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
c.WriteMessage(ircMessage),
|
c.WriteMessage(ircMessage),
|
||||||
"sending response",
|
"sending response",
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,16 +19,16 @@ type ActorScript struct {
|
||||||
Command []string `json:"command" yaml:"command"`
|
Command []string `json:"command" yaml:"command"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if len(a.Command) == 0 {
|
if len(a.Command) == 0 {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var command []string
|
var command []string
|
||||||
for _, arg := range a.Command {
|
for _, arg := range a.Command {
|
||||||
tmp, err := formatMessage(arg, m, r, nil)
|
tmp, err := formatMessage(arg, m, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "execute command argument template")
|
return false, errors.Wrap(err, "execute command argument template")
|
||||||
}
|
}
|
||||||
|
|
||||||
command = append(command, tmp)
|
command = append(command, tmp)
|
||||||
|
@ -49,7 +49,7 @@ func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
||||||
"tags": m.Tags,
|
"tags": m.Tags,
|
||||||
"username": m.User,
|
"username": m.User,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return errors.Wrap(err, "encoding script input")
|
return false, errors.Wrap(err, "encoding script input")
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, command[0], command[1:]...) // #nosec G204 // This is expected to call a command with parameters
|
cmd := exec.CommandContext(ctx, command[0], command[1:]...) // #nosec G204 // This is expected to call a command with parameters
|
||||||
|
@ -59,12 +59,12 @@ func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
||||||
cmd.Stdout = stdout
|
cmd.Stdout = stdout
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return errors.Wrap(err, "running command")
|
return false, errors.Wrap(err, "running command")
|
||||||
}
|
}
|
||||||
|
|
||||||
if stdout.Len() == 0 {
|
if stdout.Len() == 0 {
|
||||||
// Script was successful but did not yield actions
|
// Script was successful but did not yield actions
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -74,16 +74,18 @@ func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
||||||
|
|
||||||
decoder.DisallowUnknownFields()
|
decoder.DisallowUnknownFields()
|
||||||
if err := decoder.Decode(&actions); err != nil {
|
if err := decoder.Decode(&actions); err != nil {
|
||||||
return errors.Wrap(err, "decoding actions output")
|
return false, errors.Wrap(err, "decoding actions output")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
if err := triggerActions(c, m, r, action); err != nil {
|
apc, err := triggerActions(c, m, r, action)
|
||||||
return errors.Wrap(err, "execute returned action")
|
if err != nil {
|
||||||
|
return preventCooldown, errors.Wrap(err, "execute returned action")
|
||||||
}
|
}
|
||||||
|
preventCooldown = preventCooldown || apc
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return preventCooldown, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorScript) IsAsync() bool { return false }
|
func (a ActorScript) IsAsync() bool { return false }
|
||||||
|
|
|
@ -15,18 +15,18 @@ type ActorSetVariable struct {
|
||||||
Set string `json:"set" yaml:"set"`
|
Set string `json:"set" yaml:"set"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if a.Variable == "" {
|
if a.Variable == "" {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
varName, err := formatMessage(a.Variable, m, r, nil)
|
varName, err := formatMessage(a.Variable, m, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "preparing variable name")
|
return false, errors.Wrap(err, "preparing variable name")
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.Clear {
|
if a.Clear {
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
store.RemoveVariable(varName),
|
store.RemoveVariable(varName),
|
||||||
"removing variable",
|
"removing variable",
|
||||||
)
|
)
|
||||||
|
@ -34,10 +34,10 @@ func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *Rule) error
|
||||||
|
|
||||||
value, err := formatMessage(a.Set, m, r, nil)
|
value, err := formatMessage(a.Set, m, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "preparing value")
|
return false, errors.Wrap(err, "preparing value")
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
store.SetVariable(varName, value),
|
store.SetVariable(varName, value),
|
||||||
"setting variable",
|
"setting variable",
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,12 +16,12 @@ type ActorTimeout struct {
|
||||||
Timeout *time.Duration `json:"timeout" yaml:"timeout"`
|
Timeout *time.Duration `json:"timeout" yaml:"timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorTimeout) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorTimeout) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if a.Timeout == nil {
|
if a.Timeout == nil {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
c.WriteMessage(&irc.Message{
|
c.WriteMessage(&irc.Message{
|
||||||
Command: "PRIVMSG",
|
Command: "PRIVMSG",
|
||||||
Params: []string{
|
Params: []string{
|
||||||
|
|
|
@ -16,19 +16,19 @@ type ActorWhisper struct {
|
||||||
WhisperTo *string `json:"whisper_to" yaml:"whisper_to"`
|
WhisperTo *string `json:"whisper_to" yaml:"whisper_to"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ActorWhisper) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
func (a ActorWhisper) Execute(c *irc.Client, m *irc.Message, r *Rule) (preventCooldown bool, err error) {
|
||||||
if a.WhisperTo == nil || a.WhisperMessage == nil {
|
if a.WhisperTo == nil || a.WhisperMessage == nil {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
to, err := formatMessage(*a.WhisperTo, m, r, nil)
|
to, err := formatMessage(*a.WhisperTo, m, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "preparing whisper receiver")
|
return false, errors.Wrap(err, "preparing whisper receiver")
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := formatMessage(*a.WhisperMessage, m, r, nil)
|
msg, err := formatMessage(*a.WhisperMessage, m, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "preparing whisper message")
|
return false, errors.Wrap(err, "preparing whisper message")
|
||||||
}
|
}
|
||||||
|
|
||||||
channel := "#tmijs" // As a fallback, copied from tmi.js
|
channel := "#tmijs" // As a fallback, copied from tmi.js
|
||||||
|
@ -36,7 +36,7 @@ func (a ActorWhisper) Execute(c *irc.Client, m *irc.Message, r *Rule) error {
|
||||||
channel = fmt.Sprintf("#%s", config.Channels[0])
|
channel = fmt.Sprintf("#%s", config.Channels[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return false, errors.Wrap(
|
||||||
c.WriteMessage(&irc.Message{
|
c.WriteMessage(&irc.Message{
|
||||||
Command: "PRIVMSG",
|
Command: "PRIVMSG",
|
||||||
Params: []string{
|
Params: []string{
|
||||||
|
|
22
actions.go
22
actions.go
|
@ -11,7 +11,7 @@ import (
|
||||||
type (
|
type (
|
||||||
Actor interface {
|
Actor interface {
|
||||||
// Execute will be called after the config was read into the Actor
|
// Execute will be called after the config was read into the Actor
|
||||||
Execute(*irc.Client, *irc.Message, *Rule) error
|
Execute(*irc.Client, *irc.Message, *Rule) (preventCooldown bool, err error)
|
||||||
// IsAsync may return true if the Execute function is to be executed
|
// IsAsync may return true if the Execute function is to be executed
|
||||||
// in a Go routine as of long runtime. Normally it should return false
|
// in a Go routine as of long runtime. Normally it should return false
|
||||||
// except in very specific cases
|
// except in very specific cases
|
||||||
|
@ -35,7 +35,7 @@ func registerAction(af ActorCreationFunc) {
|
||||||
availableActions = append(availableActions, af)
|
availableActions = append(availableActions, af)
|
||||||
}
|
}
|
||||||
|
|
||||||
func triggerActions(c *irc.Client, m *irc.Message, rule *Rule, ra *RuleAction) error {
|
func triggerActions(c *irc.Client, m *irc.Message, rule *Rule, ra *RuleAction) (preventCooldown bool, err error) {
|
||||||
availableActionsLock.RLock()
|
availableActionsLock.RLock()
|
||||||
defer availableActionsLock.RUnlock()
|
defer availableActionsLock.RUnlock()
|
||||||
|
|
||||||
|
@ -52,30 +52,38 @@ func triggerActions(c *irc.Client, m *irc.Message, rule *Rule, ra *RuleAction) e
|
||||||
|
|
||||||
if a.IsAsync() {
|
if a.IsAsync() {
|
||||||
go func() {
|
go func() {
|
||||||
if err := a.Execute(c, m, rule); err != nil {
|
if _, err := a.Execute(c, m, rule); err != nil {
|
||||||
logger.WithError(err).Error("Error in async actor")
|
logger.WithError(err).Error("Error in async actor")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.Execute(c, m, rule); err != nil {
|
apc, err := a.Execute(c, m, rule)
|
||||||
return errors.Wrap(err, "execute action")
|
if err != nil {
|
||||||
|
return preventCooldown, errors.Wrap(err, "execute action")
|
||||||
}
|
}
|
||||||
|
preventCooldown = preventCooldown || apc
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return preventCooldown, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMessage(c *irc.Client, m *irc.Message, event *string) {
|
func handleMessage(c *irc.Client, m *irc.Message, event *string) {
|
||||||
for _, r := range config.GetMatchingRules(m, event) {
|
for _, r := range config.GetMatchingRules(m, event) {
|
||||||
|
var preventCooldown bool
|
||||||
|
|
||||||
for _, a := range r.Actions {
|
for _, a := range r.Actions {
|
||||||
if err := triggerActions(c, m, r, a); err != nil {
|
apc, err := triggerActions(c, m, r, a)
|
||||||
|
if err != nil {
|
||||||
log.WithError(err).Error("Unable to trigger action")
|
log.WithError(err).Error("Unable to trigger action")
|
||||||
}
|
}
|
||||||
|
preventCooldown = preventCooldown || apc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock command
|
// Lock command
|
||||||
|
if !preventCooldown {
|
||||||
r.setCooldown(m)
|
r.setCooldown(m)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue