plugins_core.go
Rama master · réplica de lectura del repositorio original.
package main
import (
"errors"
"fmt"
"net/http"
"slices"
"github.com/Luzifer/go_helpers/backoff"
"github.com/Luzifer/go_helpers/fieldcollection"
"github.com/gorilla/mux"
"github.com/sirupsen/logrus"
"gopkg.in/irc.v4"
"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/clip"
"github.com/Luzifer/twitch-bot/v3/internal/actors/clipdetector"
"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/delay"
"github.com/Luzifer/twitch-bot/v3/internal/actors/deleteactor"
"github.com/Luzifer/twitch-bot/v3/internal/actors/eventmod"
"github.com/Luzifer/twitch-bot/v3/internal/actors/filesay"
"github.com/Luzifer/twitch-bot/v3/internal/actors/linkdetector"
"github.com/Luzifer/twitch-bot/v3/internal/actors/linkprotect"
"github.com/Luzifer/twitch-bot/v3/internal/actors/logactor"
"github.com/Luzifer/twitch-bot/v3/internal/actors/marker"
"github.com/Luzifer/twitch-bot/v3/internal/actors/messagehook"
"github.com/Luzifer/twitch-bot/v3/internal/actors/modchannel"
"github.com/Luzifer/twitch-bot/v3/internal/actors/nuke"
"github.com/Luzifer/twitch-bot/v3/internal/actors/pin"
"github.com/Luzifer/twitch-bot/v3/internal/actors/punish"
"github.com/Luzifer/twitch-bot/v3/internal/actors/quotedb"
"github.com/Luzifer/twitch-bot/v3/internal/actors/raw"
"github.com/Luzifer/twitch-bot/v3/internal/actors/respond"
"github.com/Luzifer/twitch-bot/v3/internal/actors/shield"
"github.com/Luzifer/twitch-bot/v3/internal/actors/shoutout"
"github.com/Luzifer/twitch-bot/v3/internal/actors/spotify"
"github.com/Luzifer/twitch-bot/v3/internal/actors/stopexec"
"github.com/Luzifer/twitch-bot/v3/internal/actors/timeout"
"github.com/Luzifer/twitch-bot/v3/internal/actors/unpin"
"github.com/Luzifer/twitch-bot/v3/internal/actors/variables"
"github.com/Luzifer/twitch-bot/v3/internal/actors/vip"
"github.com/Luzifer/twitch-bot/v3/internal/actors/whisper"
"github.com/Luzifer/twitch-bot/v3/internal/apimodules/customevent"
"github.com/Luzifer/twitch-bot/v3/internal/apimodules/kofi"
"github.com/Luzifer/twitch-bot/v3/internal/apimodules/msgformat"
"github.com/Luzifer/twitch-bot/v3/internal/apimodules/overlays"
"github.com/Luzifer/twitch-bot/v3/internal/apimodules/raffle"
"github.com/Luzifer/twitch-bot/v3/internal/service/access"
"github.com/Luzifer/twitch-bot/v3/internal/template/api"
"github.com/Luzifer/twitch-bot/v3/internal/template/date"
"github.com/Luzifer/twitch-bot/v3/internal/template/numeric"
"github.com/Luzifer/twitch-bot/v3/internal/template/random"
"github.com/Luzifer/twitch-bot/v3/internal/template/slice"
"github.com/Luzifer/twitch-bot/v3/internal/template/stringfns"
"github.com/Luzifer/twitch-bot/v3/internal/template/subscriber"
twitchfns "github.com/Luzifer/twitch-bot/v3/internal/template/twitch"
"github.com/Luzifer/twitch-bot/v3/internal/template/userstate"
"github.com/Luzifer/twitch-bot/v3/pkg/database"
"github.com/Luzifer/twitch-bot/v3/pkg/event"
"github.com/Luzifer/twitch-bot/v3/pkg/twitch"
"github.com/Luzifer/twitch-bot/v3/plugins"
)
const (
defaultMaxBodyBytes = 1 << 20 // 1 MiB
ircHandleWaitRetries = 10
)
var (
corePluginRegistrations = []plugins.RegisterFunc{
// Actors
announce.Register,
ban.Register,
clip.Register,
clipdetector.Register,
commercial.Register,
counter.Register,
delay.Register,
deleteactor.Register,
eventmod.Register,
filesay.Register,
linkdetector.Register,
linkprotect.Register,
logactor.Register,
marker.Register,
messagehook.Register,
modchannel.Register,
nuke.Register,
pin.Register,
punish.Register,
quotedb.Register,
raw.Register,
respond.Register,
shield.Register,
shoutout.Register,
stopexec.Register,
timeout.Register,
unpin.Register,
variables.Register,
vip.Register,
whisper.Register,
// Template functions
api.Register,
date.Register,
numeric.Register,
random.Register,
slice.Register,
spotify.Register,
stringfns.Register,
subscriber.Register,
twitchfns.Register,
userstate.Register,
// API-only modules
customevent.Register,
kofi.Register,
msgformat.Register,
overlays.Register,
raffle.Register,
}
knownModules []string
)
func initCorePlugins() error {
args := getRegistrationArguments()
for idx, rf := range corePluginRegistrations {
if err := rf(args); err != nil {
return fmt.Errorf("registering core plugin %d: %w", idx, err)
}
}
return nil
}
func registerRoute(route plugins.HTTPRouteRegistrationArgs) error {
r := router.
PathPrefix(fmt.Sprintf("/%s/", route.Module)).
Subrouter()
if !slices.Contains(knownModules, route.Module) {
knownModules = append(knownModules, route.Module)
}
var hdl http.Handler = route.HandlerFunc
// Apply fix for GoSec G120: Limit body size
if route.MaxBodyBytes == 0 {
route.MaxBodyBytes = defaultMaxBodyBytes
}
hdl = writeBodyLimitMiddleware(hdl, route.MaxBodyBytes)
// Apply auth in case it's configured
switch {
case route.RequiresEditorsAuth:
hdl = writeAuthMiddleware(hdl, moduleConfigEditor)
case route.RequiresWriteAuth:
hdl = writeAuthMiddleware(hdl, route.Module)
}
var muxRoute *mux.Route
if route.IsPrefix {
muxRoute = r.PathPrefix(route.Path).Handler(hdl)
} else {
muxRoute = r.Handle(route.Path, hdl)
}
if route.Method != "" {
muxRoute.Methods(route.Method)
}
if !route.SkipDocumentation {
if err := registerSwaggerRoute(route); err != nil {
return fmt.Errorf("registering documentation: %w", err)
}
}
return nil
}
func getRegistrationArguments() plugins.RegistrationArguments {
return plugins.RegistrationArguments{
FormatMessage: formatMessage,
FrontendNotify: func(mt string) { frontendNotifyHooks.Ping(mt) },
GetBaseURL: func() string { return cfg.BaseURL },
GetDatabaseConnector: func() database.Connector { return db },
GetLogger: func(moduleName string) *logrus.Entry { return logrus.WithField("module", moduleName) },
GetTwitchClient: func() *twitch.Client { return twitchClient },
HasAnyPermissionForChannel: accessService.HasAnyPermissionForChannel,
HasPermissionForChannel: accessService.HasPermissionsForChannel,
RegisterActor: registerAction,
RegisterActorDocumentation: registerActorDocumentation,
RegisterAPIRoute: registerRoute,
RegisterCron: cronService.AddFunc,
RegisterCopyDatabaseFunc: registerDatabaseCopyFunc,
RegisterEventHandler: registerEventHandlers,
RegisterMessageModFunc: registerChatcommand,
RegisterRawMessageHandler: registerRawMessageHandler,
RegisterTemplateFunction: tplFuncs.Register,
SendMessage: sendMessage,
ValidateToken: authService.ValidateTokenFor,
//nolint:staticcheck // Must be defined as long as the function is still supported
CreateEvent: func(evt string, eventData *fieldcollection.FieldCollection) error {
handleMessage(ircHdl.Client(), nil, &evt, eventData)
return nil
},
CreateTypedEvent: func(evt event.Event) error {
handleTypedMessage(ircHdl.Client(), nil, evt)
return nil
},
GetModuleConfigForChannel: func(module, channel string) *fieldcollection.FieldCollection {
return config.ModuleConfig.GetChannelConfig(module, channel)
},
GetTwitchClientForChannel: func(channel string) (*twitch.Client, error) {
return accessService.GetTwitchClientForChannel(channel, access.ClientConfig{
TwitchClient: cfg.TwitchClient,
TwitchClientSecret: cfg.TwitchClientSecret,
})
},
}
}
func sendMessage(m *irc.Message) error {
err := handleChatcommandModifications(m)
switch {
case err == nil:
// There was no error, the message should be sent normally
case errors.Is(err, plugins.ErrSkipSendingMessage):
// One chatcommand handler cancelled sending the message
// (probably because it was handled otherwise)
return nil
default:
// Something in a chatcommand handler went wrong
return fmt.Errorf("handling chat commands: %w", err)
}
if err = backoff.NewBackoff().WithMaxIterations(ircHandleWaitRetries).Retry(func() error {
if ircHdl == nil {
return errors.New("irc handle not available")
}
return nil
}); err != nil {
return fmt.Errorf("waiting for IRC connection: %w", err)
}
return ircHdl.SendMessage(m)
}
func writeBodyLimitMiddleware(h http.Handler, limit int64) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Body != nil {
r.Body = http.MaxBytesReader(w, r.Body, limit)
}
h.ServeHTTP(w, r)
})
}