diff --git a/changelog.md b/changelog.md index b2730f6..28361cf 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,10 @@ # Changelog +## 4.0.0 - 2019-06-23 +### Added +- Twitch chat module +- Twitch chat handler that calculates duplicates in chat + ## 3.0.2 - 2019-06-03 ### Added - Command !subhistory diff --git a/go.mod b/go.mod index 2008330..14d0b5a 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,7 @@ module galched-bot require ( github.com/bwmarrin/discordgo v0.19.0 + github.com/gempir/go-twitch-irc v1.1.0 github.com/pkg/errors v0.8.1 github.com/stretchr/testify v1.3.0 // indirect go.uber.org/atomic v1.3.2 // indirect diff --git a/go.sum b/go.sum index 31b3e85..28ae0a3 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5a github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gempir/go-twitch-irc v1.1.0 h1:Q9gQGI/3yJzYwlYDlFsGJzWfpaqubMExfmBXNpOC6W0= +github.com/gempir/go-twitch-irc v1.1.0/go.mod h1:Pc661rsUSmkQXvI9W2bNyLt4ZrMAgHZPnVwMQEJ0fdo= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= diff --git a/main.go b/main.go index 5b7b8e0..2107535 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "galched-bot/modules/grace" "galched-bot/modules/settings" "galched-bot/modules/subday" + "galched-bot/modules/twitchat" "go.uber.org/fx" ) @@ -21,6 +22,7 @@ type ( Context context.Context Discord *discord.Discord Settings *settings.Settings + Chat *twitchat.TwitchIRC } ) @@ -35,14 +37,26 @@ func start(p appParam) error { if err != nil { log.Print("discord: cannot start instance", err) return err - } log.Printf("main: discord instance running") - log.Printf("main: — — —") + err = p.Chat.Start() + if err != nil { + log.Print("chat: cannot start instance", err) + return err + } + log.Printf("main: twitch chat instance running") + + log.Printf("main: — — —") <-p.Context.Done() log.Print("main: stopping galched-bot") + err = p.Chat.Stop() + if err != nil { + log.Print("chat: cannot stop instance", err) + return err + } + err = p.Discord.Stop() if err != nil { log.Print("discord: cannot stop instance", err) @@ -57,7 +71,7 @@ func main() { var err error app := fx.New( fx.Logger(new(silentPrinter)), - fx.Provide(settings.New, grace.New, discord.New, subday.New), + fx.Provide(settings.New, grace.New, discord.New, subday.New, twitchat.New), fx.Invoke(start)) err = app.Start(context.Background()) diff --git a/modules/settings/settings.go b/modules/settings/settings.go index a30095b..0cf6c80 100644 --- a/modules/settings/settings.go +++ b/modules/settings/settings.go @@ -6,8 +6,11 @@ import ( ) const ( - version = "3.0.2" + version = "4.0.0" + twitchUser = "galchedbot" + twitchIRCRoom = "galched" discordTokenPath = "./tokens/.discordtoken" + twitchTokenPath = "./tokens/.twitchtoken" subdayDataPath = "./backups/subday" // Permitted roles in discord for subday @@ -21,6 +24,9 @@ type ( Settings struct { Version string DiscordToken string + TwitchUser string + TwitchIRCRoom string + TwitchToken string SubdayDataPath string PermittedRoles []string } @@ -31,10 +37,17 @@ func New() (*Settings, error) { if err != nil { log.Print("settings: cannot read discord token file", err) } + twitchToken, err := ioutil.ReadFile(twitchTokenPath) + if err != nil { + log.Print("settings: cannot read twitch token file", err) + } return &Settings{ Version: version, DiscordToken: string(discordToken), + TwitchToken: string(twitchToken), + TwitchUser: twitchUser, + TwitchIRCRoom: twitchIRCRoom, SubdayDataPath: subdayDataPath, PermittedRoles: []string{subRole1, subRole2, galchedRole, smorcRole}, }, nil diff --git a/modules/twitchat/duphandler.go b/modules/twitchat/duphandler.go new file mode 100644 index 0000000..01b0b74 --- /dev/null +++ b/modules/twitchat/duphandler.go @@ -0,0 +1,48 @@ +package twitchat + +import ( + "fmt" + "log" + "strings" + + "github.com/gempir/go-twitch-irc" +) + +type ( + dupHandler struct { + lastMessage string + counter int + dupMinimal int + } +) + +const DupMinimal = 3 + +func DupHandler() MessageHandler { + return &dupHandler{ + lastMessage: "", + counter: 0, + dupMinimal: DupMinimal, + } +} + +func (h *dupHandler) IsValid(m string) bool { + return true +} + +func (h *dupHandler) Handle(ch string, u *twitch.User, m *twitch.Message, client *twitch.Client) { + data := strings.Fields(m.Text) + for i := range data { + if data[i] == h.lastMessage { + h.counter++ + } else { + if h.counter >= h.dupMinimal { + msg := fmt.Sprintf("%d %s подряд", h.counter, h.lastMessage) + client.Say(ch, msg) + log.Print("chat: ", msg) + } + h.counter = 1 + h.lastMessage = data[i] + } + } +} diff --git a/modules/twitchat/handlers.go b/modules/twitchat/handlers.go new file mode 100644 index 0000000..a2fdc50 --- /dev/null +++ b/modules/twitchat/handlers.go @@ -0,0 +1,12 @@ +package twitchat + +import ( + "github.com/gempir/go-twitch-irc" +) + +type ( + MessageHandler interface { + IsValid(string) bool + Handle(ch string, u *twitch.User, m *twitch.Message, client *twitch.Client) + } +) diff --git a/modules/twitchat/twitchat.go b/modules/twitchat/twitchat.go new file mode 100644 index 0000000..d70c376 --- /dev/null +++ b/modules/twitchat/twitchat.go @@ -0,0 +1,53 @@ +package twitchat + +import ( + "galched-bot/modules/settings" + + "github.com/gempir/go-twitch-irc" + _ "github.com/gempir/go-twitch-irc" +) + +type ( + TwitchIRC struct { + username string + chat *twitch.Client + handlers []MessageHandler + } +) + +func New(s *settings.Settings) (*TwitchIRC, error) { + var irc = new(TwitchIRC) + + irc.username = s.TwitchUser + + irc.handlers = append(irc.handlers, DupHandler()) + + irc.chat = twitch.NewClient(s.TwitchUser, s.TwitchToken) + irc.chat.OnNewMessage(irc.MessageHandler) + irc.chat.Join(s.TwitchIRCRoom) + + return irc, nil +} + +func (c *TwitchIRC) Start() error { + go func() { + err := c.chat.Connect() + _ = err // no point in error because disconnect will be called anyway + }() + return nil +} + +func (c *TwitchIRC) Stop() error { + return c.chat.Disconnect() +} + +func (c *TwitchIRC) MessageHandler(ch string, u twitch.User, m twitch.Message) { + if u.Username == c.username { + return + } + for i := range c.handlers { + if c.handlers[i].IsValid(m.Text) { + c.handlers[i].Handle(ch, &u, &m, c.chat) + } + } +}