mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-30 00:21:16 +00:00
[commercial] Add commercial
actor
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
9726948de8
commit
990060ce41
4 changed files with 180 additions and 0 deletions
132
internal/actors/commercial/actor.go
Normal file
132
internal/actors/commercial/actor.go
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
package commercial
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-irc/irc"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/Luzifer/twitch-bot/v3/pkg/twitch"
|
||||||
|
"github.com/Luzifer/twitch-bot/v3/plugins"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
actorName = "commercial"
|
||||||
|
|
||||||
|
maxCommercialDuration = 180
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
formatMessage plugins.MsgFormatter
|
||||||
|
permCheckFn plugins.ChannelPermissionCheckFunc
|
||||||
|
tcGetter func(string) (*twitch.Client, error)
|
||||||
|
|
||||||
|
commercialChatcommandRegex = regexp.MustCompile(`^/commercial ([0-9]+)$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func Register(args plugins.RegistrationArguments) error {
|
||||||
|
formatMessage = args.FormatMessage
|
||||||
|
permCheckFn = args.HasPermissionForChannel
|
||||||
|
tcGetter = args.GetTwitchClientForChannel
|
||||||
|
|
||||||
|
args.RegisterActor(actorName, func() plugins.Actor { return &actor{} })
|
||||||
|
|
||||||
|
args.RegisterActorDocumentation(plugins.ActionDocumentation{
|
||||||
|
Description: "Start Commercial",
|
||||||
|
Name: "Commercial",
|
||||||
|
Type: actorName,
|
||||||
|
|
||||||
|
Fields: []plugins.ActionDocumentationField{
|
||||||
|
{
|
||||||
|
Default: "",
|
||||||
|
Description: "Duration of the commercial (must not be longer than 180s and must yield an integer)",
|
||||||
|
Key: "duration",
|
||||||
|
Name: "Duration",
|
||||||
|
Optional: false,
|
||||||
|
SupportTemplate: true,
|
||||||
|
Type: plugins.ActionDocumentationFieldTypeString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
args.RegisterMessageModFunc("/commercial", handleChatCommand)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type actor struct{}
|
||||||
|
|
||||||
|
func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
|
||||||
|
ptrStringEmpty := func(v string) *string { return &v }("")
|
||||||
|
|
||||||
|
durationStr, err := formatMessage(attrs.MustString("duration", ptrStringEmpty), m, r, eventData)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "executing duration template")
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, startCommercial(strings.TrimLeft(plugins.DeriveChannel(m, eventData), "#"), durationStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a actor) IsAsync() bool { return false }
|
||||||
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
|
durationTemplate, err := attrs.String("duration")
|
||||||
|
if err != nil || durationTemplate == "" {
|
||||||
|
return errors.New("duration must be non-empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tplValidator(durationTemplate); err != nil {
|
||||||
|
return errors.Wrap(err, "validating duration template")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleChatCommand(m *irc.Message) error {
|
||||||
|
channel := strings.TrimLeft(plugins.DeriveChannel(m, nil), "#")
|
||||||
|
|
||||||
|
matches := commercialChatcommandRegex.FindStringSubmatch(m.Trailing())
|
||||||
|
if matches == nil {
|
||||||
|
return errors.New("ban message does not match required format")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := startCommercial(channel, matches[1]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugins.ErrSkipSendingMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func startCommercial(channel, durationStr string) error {
|
||||||
|
duration, err := strconv.ParseInt(durationStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "parsing duration to integer")
|
||||||
|
}
|
||||||
|
|
||||||
|
if duration > maxCommercialDuration {
|
||||||
|
return errors.New("duration too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := permCheckFn(channel, twitch.ScopeChannelEditCommercial)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "checking for channel permissions")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("channel %q is missing permission %s", channel, twitch.ScopeChannelEditCommercial)
|
||||||
|
}
|
||||||
|
|
||||||
|
tc, err := tcGetter(channel)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "getting channel twitch-client")
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Wrap(
|
||||||
|
tc.RunCommercial(context.Background(), channel, duration),
|
||||||
|
"running commercial",
|
||||||
|
)
|
||||||
|
}
|
|
@ -129,3 +129,36 @@ func (c *Client) RemoveChannelVIP(ctx context.Context, broadcasterName, userName
|
||||||
"executing request",
|
"executing request",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunCommercial starts a commercial on the specified channel
|
||||||
|
func (c *Client) RunCommercial(ctx context.Context, channel string, duration int64) error {
|
||||||
|
channelID, err := c.GetIDForUsername(channel)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "getting ID for channel name")
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := struct {
|
||||||
|
BroadcasterID string `json:"broadcaster_id"`
|
||||||
|
Length int64 `json:"length"`
|
||||||
|
}{
|
||||||
|
BroadcasterID: channelID,
|
||||||
|
Length: duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
body := new(bytes.Buffer)
|
||||||
|
if err := json.NewEncoder(body).Encode(payload); err != nil {
|
||||||
|
return errors.Wrap(err, "encoding payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Wrap(
|
||||||
|
c.request(clientRequestOpts{
|
||||||
|
AuthType: authTypeBearerToken,
|
||||||
|
Body: body,
|
||||||
|
Context: ctx,
|
||||||
|
Method: http.MethodPost,
|
||||||
|
OKStatus: http.StatusOK,
|
||||||
|
URL: "https://api.twitch.tv/helix/channels/commercial",
|
||||||
|
}),
|
||||||
|
"executing request",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/Luzifer/go_helpers/v2/str"
|
"github.com/Luzifer/go_helpers/v2/str"
|
||||||
"github.com/Luzifer/twitch-bot/v3/internal/actors/announce"
|
"github.com/Luzifer/twitch-bot/v3/internal/actors/announce"
|
||||||
"github.com/Luzifer/twitch-bot/v3/internal/actors/ban"
|
"github.com/Luzifer/twitch-bot/v3/internal/actors/ban"
|
||||||
|
"github.com/Luzifer/twitch-bot/v3/internal/actors/commercial"
|
||||||
"github.com/Luzifer/twitch-bot/v3/internal/actors/counter"
|
"github.com/Luzifer/twitch-bot/v3/internal/actors/counter"
|
||||||
"github.com/Luzifer/twitch-bot/v3/internal/actors/delay"
|
"github.com/Luzifer/twitch-bot/v3/internal/actors/delay"
|
||||||
deleteactor "github.com/Luzifer/twitch-bot/v3/internal/actors/delete"
|
deleteactor "github.com/Luzifer/twitch-bot/v3/internal/actors/delete"
|
||||||
|
@ -51,6 +52,7 @@ var (
|
||||||
// Actors
|
// Actors
|
||||||
announce.Register,
|
announce.Register,
|
||||||
ban.Register,
|
ban.Register,
|
||||||
|
commercial.Register,
|
||||||
counter.Register,
|
counter.Register,
|
||||||
delay.Register,
|
delay.Register,
|
||||||
deleteactor.Register,
|
deleteactor.Register,
|
||||||
|
|
|
@ -44,6 +44,19 @@ Ban user from chat
|
||||||
reason: ""
|
reason: ""
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Commercial
|
||||||
|
|
||||||
|
Start Commercial
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- type: commercial
|
||||||
|
attributes:
|
||||||
|
# Duration of the commercial (must not be longer than 180s and must yield an integer)
|
||||||
|
# Optional: false
|
||||||
|
# Type: string (Supports Templating)
|
||||||
|
duration: ""
|
||||||
|
```
|
||||||
|
|
||||||
## Custom Event
|
## Custom Event
|
||||||
|
|
||||||
Create a custom event
|
Create a custom event
|
||||||
|
|
Loading…
Reference in a new issue