diff --git a/config/config.go b/config/config.go index 112d279..5809471 100644 --- a/config/config.go +++ b/config/config.go @@ -37,6 +37,10 @@ type Config struct { Freq int Channels []string } + Twitch struct { + Freq int + Users map[string][]string //channel -> usernames + } EnforceNicks bool WelcomeMsgs []string TwitterConsumerKey string diff --git a/main.go b/main.go index 6d8f86d..3f4815f 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ import ( "github.com/velour/catbase/plugins/leftpad" "github.com/velour/catbase/plugins/reminder" "github.com/velour/catbase/plugins/talker" + "github.com/velour/catbase/plugins/twitch" "github.com/velour/catbase/plugins/your" "github.com/velour/catbase/plugins/zork" "github.com/velour/catbase/slack" @@ -56,6 +57,7 @@ func main() { b.AddHandler("counter", counter.New(b)) b.AddHandler("reminder", reminder.New(b)) b.AddHandler("babbler", babbler.New(b)) + b.AddHandler("twitch", twitch.New(b)) b.AddHandler("zork", zork.New(b)) // catches anything left, will always return true b.AddHandler("factoid", fact.New(b)) diff --git a/plugins/twitch/twitch.go b/plugins/twitch/twitch.go new file mode 100644 index 0000000..be3f96f --- /dev/null +++ b/plugins/twitch/twitch.go @@ -0,0 +1,196 @@ +package twitch + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" + "strings" + "time" + + "github.com/velour/catbase/bot" + "github.com/velour/catbase/bot/msg" + "github.com/velour/catbase/config" +) + +type TwitchPlugin struct { + Bot bot.Bot + config *config.Config + twitchList map[string]*Twitcher +} + +type Twitcher struct { + name string + game string +} + +type Stream struct { + Stream struct { + Game string `json:game` + } `json:stream,omitempty` +} + +func New(bot bot.Bot) *TwitchPlugin { + p := &TwitchPlugin{ + Bot: bot, + config: bot.Config(), + twitchList: map[string]*Twitcher{}, + } + + for _, users := range p.config.Twitch.Users { + for _, twitcherName := range users { + if _, ok := p.twitchList[twitcherName]; !ok { + p.twitchList[twitcherName] = &Twitcher{ + name: twitcherName, + game: "", + } + } + } + } + + for channel := range p.config.Twitch.Users { + go p.twitchLoop(channel) + } + + return p +} + +func (p *TwitchPlugin) BotMessage(message msg.Message) bool { + return false +} + +func (p *TwitchPlugin) RegisterWeb() *string { + return nil +} + +func (p *TwitchPlugin) Message(message msg.Message) bool { + if strings.ToLower(message.Body) == "twitch status" { + channel := message.Channel + if _, ok := p.config.Twitch.Users[channel]; ok { + for _, twitcherName := range p.config.Twitch.Users[channel] { + if _, ok = p.twitchList[twitcherName]; ok { + p.checkTwitch(channel, p.twitchList[twitcherName], true) + } + } + } + return true + } + + return false +} + +func (p *TwitchPlugin) Event(kind string, message msg.Message) bool { + return false +} + +func (p *TwitchPlugin) LoadData() { + +} + +func (p *TwitchPlugin) Help(channel string, parts []string) { + msg := "There's no help for you here." + p.Bot.SendMessage(channel, msg) +} + +func (p *TwitchPlugin) twitchLoop(channel string) { + frequency := p.config.Twitch.Freq + + log.Println("Checking every ", frequency, " seconds") + + for { + time.Sleep(time.Duration(frequency) * time.Second) + // auth := p.getTwitchStreams(channel) + // if !auth { + // p.Bot.SendMessage(channel, "OAuth for catbase twitch account has expired. Renew it!") + // } + + for _, twitcherName := range p.config.Twitch.Users[channel] { + p.checkTwitch(channel, p.twitchList[twitcherName], false) + } + } +} + +func getRequest(url string) ([]byte, bool) { + resp, err := http.Get(url) + if err != nil { + log.Println(err) + return []byte{}, false + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println(err) + return []byte{}, false + } + return body, true +} + +func (p *TwitchPlugin) checkTwitch(channel string, twitcher *Twitcher, alwaysPrintStatus bool) { + baseUrl := "https://api.twitch.tv/kraken/streams/" + + body, ok := getRequest(baseUrl + twitcher.name) + if !ok { + return + } + + var stream Stream + err := json.Unmarshal(body, &stream) + if err != nil { + log.Println(err) + return + } + + game := stream.Stream.Game + if alwaysPrintStatus { + if game == "" { + p.Bot.SendMessage(channel, twitcher.name+" is not streaming.") + } else { + p.Bot.SendMessage(channel, twitcher.name+" is streaming "+game+".") + } + } else if game == "" { + if twitcher.game != "" { + p.Bot.SendMessage(channel, twitcher.name+" just stopped streaming.") + } + twitcher.game = "" + } else { + if twitcher.game != game { + p.Bot.SendMessage(channel, twitcher.name+" just started streaming "+game+".") + } + twitcher.game = game + } +} + + +// func (p *TwitchPlugin) getTwitchStreams(channel string) bool { +// token := p.Bot.config.Twitch.Token +// if token == "" || token == "" { +// log.Println("No Twitch token, cannot enable plugin.") +// return false +// } + +// access_token := "?access_token=" + token + "&scope=user_subscriptions" +// baseUrl := "https://api.twitch.tv/kraken/streams/followed" + +// body, ok := getRequest(baseUrl + access_token) +// if !ok { +// return false +// } + +// var subscriptions Subscriptions +// err := json.Unmarshal(body, &subscriptions) +// if err != nil { +// log.Println(err) +// return false +// } + +// for _, subscription := range subscriptions.Follows { +// twitcherName := subscription.Channel.Name +// if _, ok := p.twitchList[twitcherName]; !ok { +// p.twitchList[twitcherName] = &Twitcher { +// name : twitcherName, +// game : "", +// } +// } +// } +// return true +// } \ No newline at end of file diff --git a/plugins/twitch/twitch_test.go b/plugins/twitch/twitch_test.go new file mode 100644 index 0000000..4a9522d --- /dev/null +++ b/plugins/twitch/twitch_test.go @@ -0,0 +1,46 @@ +// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors. + +package twitch + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/velour/catbase/bot" + "github.com/velour/catbase/bot/msg" + "github.com/velour/catbase/bot/user" +) + +func makeMessage(payload string) msg.Message { + isCmd := strings.HasPrefix(payload, "!") + if isCmd { + payload = payload[1:] + } + return msg.Message{ + User: &user.User{Name: "tester"}, + Channel: "test", + Body: payload, + Command: isCmd, + } +} + +func makeTwitchPlugin(t *testing.T) (*TwitchPlugin, *bot.MockBot) { + mb := bot.NewMockBot() + c := New(mb) + c.config.Twitch.Users = map[string][]string{ "test" : []string{"drseabass"}} + assert.NotNil(t, c) + + c.twitchList["drseabass"] = &Twitcher{ + name: "drseabass", + game: "", + } + + return c, mb +} + +func TestTwitch(t *testing.T) { + b, mb := makeTwitchPlugin(t) + b.Message(makeMessage("!twitch status")) + assert.NotEmpty(t, mb.Messages) +}