2024-01-01 16:52:18 +00:00
|
|
|
// Package vip contains actors to modify VIPs of a channel
|
2022-12-05 17:58:49 +00:00
|
|
|
package vip
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-04-03 19:00:28 +00:00
|
|
|
"fmt"
|
2022-12-05 17:58:49 +00:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2023-09-11 17:51:38 +00:00
|
|
|
"gopkg.in/irc.v4"
|
2022-12-05 17:58:49 +00:00
|
|
|
|
2024-04-03 19:00:28 +00:00
|
|
|
"github.com/Luzifer/go_helpers/v2/fieldcollection"
|
|
|
|
"github.com/Luzifer/twitch-bot/v3/internal/helpers"
|
2022-12-05 17:58:49 +00:00
|
|
|
"github.com/Luzifer/twitch-bot/v3/pkg/twitch"
|
|
|
|
"github.com/Luzifer/twitch-bot/v3/plugins"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
formatMessage plugins.MsgFormatter
|
|
|
|
permCheckFn plugins.ChannelPermissionCheckFunc
|
|
|
|
tcGetter func(string) (*twitch.Client, error)
|
|
|
|
)
|
|
|
|
|
2024-01-01 16:52:18 +00:00
|
|
|
// Register provides the plugins.RegisterFunc
|
2022-12-05 17:58:49 +00:00
|
|
|
func Register(args plugins.RegistrationArguments) error {
|
|
|
|
formatMessage = args.FormatMessage
|
|
|
|
permCheckFn = args.HasPermissionForChannel
|
|
|
|
tcGetter = args.GetTwitchClientForChannel
|
|
|
|
|
|
|
|
args.RegisterActor("vip", func() plugins.Actor { return &vipActor{} })
|
|
|
|
args.RegisterActor("unvip", func() plugins.Actor { return &unvipActor{} })
|
|
|
|
|
|
|
|
args.RegisterActorDocumentation(plugins.ActionDocumentation{
|
|
|
|
Description: "Add VIP for the given channel",
|
|
|
|
Name: "Add VIP",
|
|
|
|
Type: "vip",
|
|
|
|
|
|
|
|
Fields: []plugins.ActionDocumentationField{
|
|
|
|
{
|
|
|
|
Default: "",
|
|
|
|
Description: "Channel to add the VIP to",
|
|
|
|
Key: "channel",
|
|
|
|
Name: "Channel",
|
|
|
|
Optional: false,
|
|
|
|
SupportTemplate: true,
|
|
|
|
Type: plugins.ActionDocumentationFieldTypeString,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Default: "",
|
|
|
|
Description: "User to add as VIP",
|
|
|
|
Key: "user",
|
|
|
|
Name: "User",
|
|
|
|
Optional: false,
|
|
|
|
SupportTemplate: true,
|
|
|
|
Type: plugins.ActionDocumentationFieldTypeString,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
args.RegisterActorDocumentation(plugins.ActionDocumentation{
|
|
|
|
Description: "Remove VIP for the given channel",
|
|
|
|
Name: "Remove VIP",
|
|
|
|
Type: "unvip",
|
|
|
|
|
|
|
|
Fields: []plugins.ActionDocumentationField{
|
|
|
|
{
|
|
|
|
Default: "",
|
|
|
|
Description: "Channel to remove the VIP from",
|
|
|
|
Key: "channel",
|
|
|
|
Name: "Channel",
|
|
|
|
Optional: false,
|
|
|
|
SupportTemplate: true,
|
|
|
|
Type: plugins.ActionDocumentationFieldTypeString,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Default: "",
|
|
|
|
Description: "User to remove as VIP",
|
|
|
|
Key: "user",
|
|
|
|
Name: "User",
|
|
|
|
Optional: false,
|
|
|
|
SupportTemplate: true,
|
|
|
|
Type: plugins.ActionDocumentationFieldTypeString,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
args.RegisterMessageModFunc("/vip", handleAddVIP)
|
|
|
|
args.RegisterMessageModFunc("/unvip", handleRemoveVIP)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Actor
|
|
|
|
|
|
|
|
type (
|
|
|
|
actor struct{}
|
|
|
|
unvipActor struct{ actor }
|
|
|
|
vipActor struct{ actor }
|
|
|
|
)
|
|
|
|
|
|
|
|
func (actor) IsAsync() bool { return false }
|
2024-04-03 19:00:28 +00:00
|
|
|
func (actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *fieldcollection.FieldCollection) (err error) {
|
|
|
|
if err = attrs.ValidateSchema(
|
|
|
|
fieldcollection.MustHaveField(fieldcollection.SchemaField{Name: "channel", NonEmpty: true, Type: fieldcollection.SchemaFieldTypeString}),
|
|
|
|
fieldcollection.MustHaveField(fieldcollection.SchemaField{Name: "user", NonEmpty: true, Type: fieldcollection.SchemaFieldTypeString}),
|
2024-04-08 13:56:12 +00:00
|
|
|
fieldcollection.MustHaveNoUnknowFields,
|
2024-04-03 19:00:28 +00:00
|
|
|
helpers.SchemaValidateTemplateField(tplValidator, "channel", "user"),
|
|
|
|
); err != nil {
|
|
|
|
return fmt.Errorf("validating attributes: %w", err)
|
2022-12-05 17:58:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-04-03 19:00:28 +00:00
|
|
|
func (actor) getParams(m *irc.Message, r *plugins.Rule, eventData *fieldcollection.FieldCollection, attrs *fieldcollection.FieldCollection) (channel, user string, err error) {
|
2022-12-05 17:58:49 +00:00
|
|
|
if channel, err = formatMessage(attrs.MustString("channel", nil), m, r, eventData); err != nil {
|
|
|
|
return "", "", errors.Wrap(err, "parsing channel")
|
|
|
|
}
|
|
|
|
|
|
|
|
if user, err = formatMessage(attrs.MustString("user", nil), m, r, eventData); err != nil {
|
|
|
|
return "", "", errors.Wrap(err, "parsing user")
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.TrimLeft(channel, "#"), user, nil
|
|
|
|
}
|
|
|
|
|
2024-04-03 19:00:28 +00:00
|
|
|
func (u unvipActor) Execute(_ *irc.Client, m *irc.Message, r *plugins.Rule, eventData *fieldcollection.FieldCollection, attrs *fieldcollection.FieldCollection) (preventCooldown bool, err error) {
|
2022-12-05 17:58:49 +00:00
|
|
|
channel, user, err := u.getParams(m, r, eventData, attrs)
|
|
|
|
if err != nil {
|
|
|
|
return false, errors.Wrap(err, "getting parameters")
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, errors.Wrap(
|
2024-01-01 16:52:18 +00:00
|
|
|
executeModVIP(channel, func(tc *twitch.Client) error {
|
|
|
|
return errors.Wrap(tc.RemoveChannelVIP(context.Background(), channel, user), "removing VIP")
|
|
|
|
}),
|
2022-12-05 17:58:49 +00:00
|
|
|
"removing VIP",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (unvipActor) Name() string { return "unvip" }
|
|
|
|
|
2024-04-03 19:00:28 +00:00
|
|
|
func (v vipActor) Execute(_ *irc.Client, m *irc.Message, r *plugins.Rule, eventData *fieldcollection.FieldCollection, attrs *fieldcollection.FieldCollection) (preventCooldown bool, err error) {
|
2022-12-05 17:58:49 +00:00
|
|
|
channel, user, err := v.getParams(m, r, eventData, attrs)
|
|
|
|
if err != nil {
|
|
|
|
return false, errors.Wrap(err, "getting parameters")
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, errors.Wrap(
|
2024-01-01 16:52:18 +00:00
|
|
|
executeModVIP(channel, func(tc *twitch.Client) error {
|
|
|
|
return errors.Wrap(tc.AddChannelVIP(context.Background(), channel, user), "adding VIP")
|
|
|
|
}),
|
2022-12-05 17:58:49 +00:00
|
|
|
"adding VIP",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vipActor) Name() string { return "vip" }
|
|
|
|
|
|
|
|
// Generic helper
|
|
|
|
|
|
|
|
func executeModVIP(channel string, modFn func(tc *twitch.Client) error) error {
|
|
|
|
ok, err := permCheckFn(channel, twitch.ScopeChannelManageVIPS)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "checking for channel permissions")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
return errors.Errorf("channel %q is missing permission %s", channel, twitch.ScopeChannelManageVIPS)
|
|
|
|
}
|
|
|
|
|
|
|
|
tc, err := tcGetter(channel)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "getting channel twitch-client")
|
|
|
|
}
|
|
|
|
|
|
|
|
return modFn(tc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Chat-Commands
|
|
|
|
|
|
|
|
func handleAddVIP(m *irc.Message) error {
|
|
|
|
return handleModVIP(m, func(tc *twitch.Client, channel, user string) error {
|
|
|
|
return errors.Wrap(tc.AddChannelVIP(context.Background(), channel, user), "adding VIP")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleModVIP(m *irc.Message, modFn func(tc *twitch.Client, channel, user string) error) error {
|
|
|
|
channel := strings.TrimLeft(plugins.DeriveChannel(m, nil), "#")
|
|
|
|
|
|
|
|
parts := strings.Split(m.Trailing(), " ")
|
|
|
|
if len(parts) != 2 { //nolint:gomnd // Just a count, makes no sense as a constant
|
|
|
|
return errors.Errorf("wrong command usage, must consist of 2 words")
|
|
|
|
}
|
|
|
|
|
|
|
|
return executeModVIP(channel, func(tc *twitch.Client) error { return modFn(tc, channel, parts[1]) })
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleRemoveVIP(m *irc.Message) error {
|
|
|
|
return handleModVIP(m, func(tc *twitch.Client, channel, user string) error {
|
|
|
|
return errors.Wrap(tc.RemoveChannelVIP(context.Background(), channel, user), "removing VIP")
|
|
|
|
})
|
|
|
|
}
|