diff --git a/backend/controller/chat/controller.go b/backend/controller/chat/controller.go index a04e5e8..de12a59 100644 --- a/backend/controller/chat/controller.go +++ b/backend/controller/chat/controller.go @@ -37,8 +37,13 @@ func (ctrl *Controller) Messages(c echo.Context) error { Map(store.GetMessageStore().All(), func(message store.MessageInterface) Node { var containerStyle Node var userStyle Node + var originIcon Node if message.Origin() == store.MessageOriginOwncast { + } + + switch message.Origin() { + case store.MessageOriginOwncast: msg := message.(store.OwncastMessage) containerStyle = StyleAttr(fmt.Sprintf( @@ -50,6 +55,10 @@ func (ctrl *Controller) Messages(c echo.Context) error { "color: var(--theme-color-users-%d)", msg.WebhookMessage.User.DisplayColor, )) + + originIcon = Img(Src(view.Asset("static/img/owncast.png")), Class("message-origin")) + case store.MessageOriginTwitch: + originIcon = Img(Src(view.Asset("static/img/twitch.png")), Class("message-origin")) } return Div( @@ -59,7 +68,7 @@ func (ctrl *Controller) Messages(c echo.Context) error { Div( Class("message-user"), userStyle, - Text(message.Author()), + Group([]Node{originIcon, Text(message.Author())}), ), Div( Class("message-body"), diff --git a/backend/controller/webhook/owncast/chat_message.go b/backend/controller/webhook/owncast/chat_message.go index cb9b7c1..b453f1c 100644 --- a/backend/controller/webhook/owncast/chat_message.go +++ b/backend/controller/webhook/owncast/chat_message.go @@ -1,11 +1,13 @@ package owncast import ( + "fmt" "net/http" "github.com/labstack/echo/v4" "gitnet.fr/deblan/owncast-webhook/backend/store" - "gitnet.fr/deblan/owncast-webhook/backend/webhook" + webhook "gitnet.fr/deblan/owncast-webhook/backend/webhook/owncast" + "gitnet.fr/deblan/owncast-webhook/config" ) type Controller struct { @@ -14,7 +16,7 @@ type Controller struct { func New(e *echo.Echo) *Controller { c := Controller{} - e.POST("/webhook/owncast/chat_message", c.ChatMessage) + e.POST(fmt.Sprintf("/webhook/%s/owncast/chat_message", config.Get().Server.WebhookSecret), c.ChatMessage) return &c } diff --git a/backend/store/owncast_message.go b/backend/store/owncast_message.go index 70a0ce2..0d0cc08 100644 --- a/backend/store/owncast_message.go +++ b/backend/store/owncast_message.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "gitnet.fr/deblan/owncast-webhook/backend/webhook" + webhook "gitnet.fr/deblan/owncast-webhook/backend/webhook/owncast" "gitnet.fr/deblan/owncast-webhook/config" ) diff --git a/backend/store/twitch_message.go b/backend/store/twitch_message.go new file mode 100644 index 0000000..9c53062 --- /dev/null +++ b/backend/store/twitch_message.go @@ -0,0 +1,27 @@ +package store + +import "github.com/gempir/go-twitch-irc/v4" + +type TwitchMessage struct { + Message twitch.PrivateMessage +} + +func (o TwitchMessage) ID() string { + return o.Message.ID +} + +func (o TwitchMessage) Visible() bool { + return true +} + +func (o TwitchMessage) Origin() MessageOrigin { + return MessageOriginTwitch +} + +func (o TwitchMessage) Author() string { + return o.Message.User.DisplayName +} + +func (o TwitchMessage) Content() string { + return o.Message.Message +} diff --git a/backend/webhook/message.go b/backend/webhook/owncast/message.go similarity index 100% rename from backend/webhook/message.go rename to backend/webhook/owncast/message.go diff --git a/cmd/server/server.go b/cmd/server/server.go index 39e6356..485b7d8 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -14,6 +14,7 @@ import ( "github.com/labstack/echo/v4/middleware" "gitnet.fr/deblan/owncast-webhook/backend/router" "gitnet.fr/deblan/owncast-webhook/config" + "gitnet.fr/deblan/owncast-webhook/twitch" ) type TemplateRenderer struct { @@ -42,6 +43,10 @@ func main() { e.Use(middleware.Logger()) router.RegisterControllers(e) + if conf.Twitch.Enable { + twitch.IrcClient() + } + if err := e.Start(fmt.Sprintf("%s:%d", conf.Server.Address, conf.Server.Port)); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatal(err) } diff --git a/config.ini.example b/config.ini.example new file mode 100644 index 0000000..9be2b6d --- /dev/null +++ b/config.ini.example @@ -0,0 +1,12 @@ +[server] +port = 1926 +address = "0.0.0.0" +base_url = "https://api.example.com" +webhook_secret = "86f9ac485ffaf1c24f03e3fd441f65d9" + +[owncast] +base_url = "https://live.example.com" + +[twitch] +enable = false +channel = "username" diff --git a/config/config.go b/config/config.go index 9a65f42..9b020a2 100644 --- a/config/config.go +++ b/config/config.go @@ -8,18 +8,17 @@ import ( type Config struct { Server struct { - BaseUrl string - Address string - Port int + BaseUrl string + Address string + WebhookSecret string + Port int } Owncast struct { BaseUrl string } Twitch struct { - ClientId string - ClientSecret string - WebhookUrl string - WebhookSecret string + Enable bool + Channel string } } @@ -43,11 +42,10 @@ func (c *Config) Load(file string) { config.Server.Address = cfg.Section("server").Key("address").String() config.Server.Port, _ = cfg.Section("server").Key("port").Int() config.Server.BaseUrl = cfg.Section("server").Key("base_url").String() + config.Server.WebhookSecret = cfg.Section("server").Key("webhook_secret").String() config.Owncast.BaseUrl = cfg.Section("owncast").Key("base_url").String() - config.Twitch.ClientId = cfg.Section("twitch").Key("client_id").String() - config.Twitch.ClientSecret = cfg.Section("twitch").Key("client_secret").String() - config.Twitch.WebhookSecret = cfg.Section("twitch").Key("webhook_secret").String() - config.Twitch.WebhookUrl = cfg.Section("twitch").Key("webhook_url").String() + config.Twitch.Enable = cfg.Section("twitch").Key("enable").MustBool(false) + config.Twitch.Channel = cfg.Section("twitch").Key("channel").String() } diff --git a/frontend/img/owncast.png b/frontend/img/owncast.png new file mode 100644 index 0000000..a7f1de9 Binary files /dev/null and b/frontend/img/owncast.png differ diff --git a/frontend/img/twitch.png b/frontend/img/twitch.png new file mode 100644 index 0000000..7bb99fa Binary files /dev/null and b/frontend/img/twitch.png differ diff --git a/frontend/scss/main.scss b/frontend/scss/main.scss index 91ccb26..2bee2f2 100644 --- a/frontend/scss/main.scss +++ b/frontend/scss/main.scss @@ -36,6 +36,12 @@ border-radius: 5px; border: 2px solid #00ced1; padding: 10px; + + .message-origin { + height: 15px; + vertical-align: middle; + margin-right: 4px; + } .message-user { color: #58d63c; diff --git a/go.mod b/go.mod index 0d15abe..5aefb9e 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.23.0 require ( github.com/GeertJohan/go.rice v1.0.3 github.com/a-h/templ v0.2.778 + github.com/gempir/go-twitch-irc/v4 v4.2.0 github.com/go-playground/validator v9.31.0+incompatible github.com/labstack/echo/v4 v4.12.0 gopkg.in/ini.v1 v1.67.0 @@ -23,6 +24,7 @@ require ( github.com/stretchr/testify v1.9.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/yassinebenaid/godump v0.11.1 // indirect golang.org/x/crypto v0.27.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.25.0 // indirect diff --git a/go.sum b/go.sum index 3a306d4..da1c362 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/daaku/go.zipexe v1.0.2 h1:Zg55YLYTr7M9wjKn8SY/WcpuuEi+kR2u4E8RhvpyXmk github.com/daaku/go.zipexe v1.0.2/go.mod h1:5xWogtqlYnfBXkSB1o9xysukNP9GTvaNkqzUZbt3Bw8= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gempir/go-twitch-irc/v4 v4.2.0 h1:OCeff+1aH4CZIOxgKOJ8dQjh+1ppC6sLWrXOcpGZyq4= +github.com/gempir/go-twitch-irc/v4 v4.2.0/go.mod h1:QsOMMAk470uxQ7EYD9GJBGAVqM/jDrXBNbuePfTauzg= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -40,6 +42,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yassinebenaid/godump v0.11.1 h1:SPujx/XaYqGDfmNh7JI3dOyCUVrG0bG2duhO3Eh2EhI= +github.com/yassinebenaid/godump v0.11.1/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= diff --git a/twitch/irc_client.go b/twitch/irc_client.go new file mode 100644 index 0000000..ca37952 --- /dev/null +++ b/twitch/irc_client.go @@ -0,0 +1,29 @@ +package twitch + +import ( + "log" + + tw "github.com/gempir/go-twitch-irc/v4" + "gitnet.fr/deblan/owncast-webhook/backend/store" + "gitnet.fr/deblan/owncast-webhook/config" +) + +func IrcClient() { + client := tw.NewAnonymousClient() + + client.OnPrivateMessage(func(message tw.PrivateMessage) { + store.GetMessageStore().Add(store.TwitchMessage{ + Message: message, + }) + }) + + client.Join(config.Get().Twitch.Channel) + + go func() { + err := client.Connect() + + if err != nil { + log.Fatal(err.Error()) + } + }() +}