From c5d468c7239fdb5067fbe17abb7dd43d79f4982a Mon Sep 17 00:00:00 2001 From: Chris Sexton Date: Wed, 28 Apr 2021 16:48:02 -0400 Subject: [PATCH] last: add multichannel support This * ensures the last is between yesterday and yesterday * adds a channel check to the query * adds a command to check a specific channel --- bot/interfaces.go | 6 +++++ connectors/discord/discord.go | 23 +++++++++++++++++ connectors/irc/irc.go | 10 ++++++++ connectors/slack/slack.go | 27 +++++++++++++++++--- connectors/slackapp/slackApp.go | 20 +++++++++++++++ plugins/cli/cli.go | 2 ++ plugins/last/last.go | 45 +++++++++++++++++++++------------ 7 files changed, 114 insertions(+), 19 deletions(-) diff --git a/bot/interfaces.go b/bot/interfaces.go index cfb2450..cf17947 100644 --- a/bot/interfaces.go +++ b/bot/interfaces.go @@ -189,6 +189,12 @@ type Connector interface { // Translate emojy to/from services Emojy(string) string + + // GetChannelName returns the human-friendly name for an ID (if possible) + GetChannelName(id string) string + + // GetChannelName returns the channel ID for a human-friendly name (if possible) + GetChannelID(id string) string } // Plugin interface used for compatibility with the Plugin interface diff --git a/connectors/discord/discord.go b/connectors/discord/discord.go index a1b75dc..7f15b9e 100644 --- a/connectors/discord/discord.go +++ b/connectors/discord/discord.go @@ -232,3 +232,26 @@ func (d *Discord) Emojy(name string) string { func (d *Discord) URLFormat(title, url string) string { return fmt.Sprintf("%s (%s)", title, url) } + +// GetChannelName returns the channel ID for a human-friendly name (if possible) +func (d *Discord) GetChannelID(name string) string { + chs, err := d.client.UserChannels() + if err != nil { + return name + } + for _, ch := range chs { + if ch.Name == name { + return ch.ID + } + } + return name +} + +// GetChannelName returns the human-friendly name for an ID (if possible) +func (d *Discord) GetChannelName(id string) string { + ch, err := d.client.Channel(id) + if err != nil { + return id + } + return ch.Name +} diff --git a/connectors/irc/irc.go b/connectors/irc/irc.go index 489f264..800146a 100644 --- a/connectors/irc/irc.go +++ b/connectors/irc/irc.go @@ -326,3 +326,13 @@ func (i Irc) Emojy(name string) string { } return name } + +// GetChannelName returns the channel ID for a human-friendly name (if possible) +func (i Irc) GetChannelID(name string) string { + return name +} + +// GetChannelName returns the human-friendly name for an ID (if possible) +func (i Irc) GetChannelName(id string) string { + return id +} diff --git a/connectors/slack/slack.go b/connectors/slack/slack.go index 8cb018b..1e30c8d 100644 --- a/connectors/slack/slack.go +++ b/connectors/slack/slack.go @@ -4,6 +4,8 @@ package slack import ( + // "sync/atomic" + "context" "encoding/json" "errors" "fmt" @@ -15,9 +17,6 @@ import ( "regexp" "strconv" "strings" - - // "sync/atomic" - "context" "time" "github.com/rs/zerolog/log" @@ -735,6 +734,28 @@ func (s *Slack) Profile(string) (user.User, error) { return user.User{}, fmt.Errorf("unimplemented") } +// GetChannelName returns the channel ID for a human-friendly name (if possible) +func (s *Slack) GetChannelID(name string) string { + chs := s.getAllChannels() + for _, ch := range chs { + if ch.Name == name { + return ch.ID + } + } + return name +} + +// GetChannelName returns the human-friendly name for an ID (if possible) +func (s *Slack) GetChannelName(id string) string { + chs := s.getAllChannels() + for _, ch := range chs { + if ch.ID == id { + return ch.Name + } + } + return id +} + func (s *Slack) Emojy(name string) string { e := s.config.GetMap("slack.emojy", map[string]string{}) if emojy, ok := e[name]; ok { diff --git a/connectors/slackapp/slackApp.go b/connectors/slackapp/slackApp.go index b121da7..d723c4b 100644 --- a/connectors/slackapp/slackApp.go +++ b/connectors/slackapp/slackApp.go @@ -499,6 +499,26 @@ func (s *SlackApp) getChannel(id string) (*slack.Channel, error) { return s.channels[id], nil } +// GetChannelName returns the channel ID for a human-friendly name (if possible) +func (s *SlackApp) GetChannelID(name string) string { + for _, ch := range s.channels { + if ch.Name == name { + return ch.ID + } + } + return name +} + +// GetChannelName returns the human-friendly name for an ID (if possible) +func (s *SlackApp) GetChannelName(id string) string { + ch, err := s.getChannel(id) + if err != nil { + log.Error().Err(err).Msgf("Could not get channel name for %s", id) + return id + } + return ch.Name +} + func stringForUser(user *slack.User) string { if user.Profile.DisplayName != "" { return user.Profile.DisplayName diff --git a/plugins/cli/cli.go b/plugins/cli/cli.go index 53505f2..119c383 100644 --- a/plugins/cli/cli.go +++ b/plugins/cli/cli.go @@ -121,3 +121,5 @@ func (p *CliPlugin) Emojy(name string) string { return name } func (p *CliPlugin) URLFormat(title, url string) string { return fmt.Sprintf("%s (%s)", title, url) } +func (p *CliPlugin) GetChannelName(id string) string { return id } +func (p *CliPlugin) GetChannelID(name string) string { return name } diff --git a/plugins/last/last.go b/plugins/last/last.go index 2e43bdc..879538c 100644 --- a/plugins/last/last.go +++ b/plugins/last/last.go @@ -58,18 +58,24 @@ func (p *LastPlugin) migrate() error { func (p *LastPlugin) register() { p.handlers = bot.HandlerTable{ - { - Kind: bot.Message, IsCmd: false, - Regex: regexp.MustCompile(`.*`), - HelpText: "Last does secret stuff you don't need to know about.", - Handler: p.recordLast, - }, { Kind: bot.Message, IsCmd: true, Regex: regexp.MustCompile(`(?i)^who killed the channel\??$`), HelpText: "Find out who had last yesterday", Handler: p.whoKilled, }, + { + Kind: bot.Message, IsCmd: true, + Regex: regexp.MustCompile(`(?i)^who killed #?(?P\S+)\??$`), + HelpText: "Find out who had last yesterday in a channel", + Handler: p.whoKilledChannel, + }, + { + Kind: bot.Message, IsCmd: false, + Regex: regexp.MustCompile(`.*`), + HelpText: "Last does secret stuff you don't need to know about.", + Handler: p.recordLast, + }, } p.b.RegisterTable(p, p.handlers) } @@ -133,11 +139,12 @@ type last struct { Message string } -func (p *LastPlugin) yesterdaysLast() (last, error) { +func (p *LastPlugin) yesterdaysLast(ch string) (last, error) { l := last{} midnight := first.Midnight(time.Now()) - q := `select * from last where day < ? order by day limit 1` - err := p.db.Get(&l, q, midnight) + q := `select * from last where channel = ? and day < ? and day >= ? order by day limit 1` + log.Debug().Str("q", q).Msgf("yesterdaysLast: %d to %d", midnight.Unix(), midnight.Add(-24*time.Hour).Unix()) + err := p.db.Get(&l, q, ch, midnight.Unix(), midnight.Add(-24*time.Hour).Unix()) if err != nil { return l, err } @@ -146,27 +153,33 @@ func (p *LastPlugin) yesterdaysLast() (last, error) { func (p *LastPlugin) reportLast(ch string) func() { return func() { - p.sayLast(p.b.DefaultConnector(), ch) + p.sayLast(p.b.DefaultConnector(), ch, ch) time.AfterFunc(24*time.Hour, p.reportLast(ch)) } } func (p *LastPlugin) whoKilled(r bot.Request) bool { - p.sayLast(r.Conn, r.Msg.Channel) + p.sayLast(r.Conn, r.Msg.Channel, r.Msg.Channel) return true } -func (p *LastPlugin) sayLast(c bot.Connector, ch string) { - l, err := p.yesterdaysLast() +func (p *LastPlugin) whoKilledChannel(r bot.Request) bool { + ch := r.Values["channel"] + p.sayLast(r.Conn, r.Conn.GetChannelID(ch), r.Msg.Channel) + return true +} + +func (p *LastPlugin) sayLast(c bot.Connector, chFrom, chTo string) { + l, err := p.yesterdaysLast(chFrom) if err != nil { log.Error().Err(err).Msgf("Couldn't find last") - p.b.Send(c, bot.Message, ch, "I couldn't find a last.") + p.b.Send(c, bot.Message, chTo, "I couldn't find a last.") return } if l.Day == 0 { log.Error().Interface("l", l).Msgf("Couldn't find last") - p.b.Send(c, bot.Message, ch, "I couldn't find a last.") + p.b.Send(c, bot.Message, chTo, "I couldn't find a last.") } msg := fmt.Sprintf(`%s killed the channel last night by saying "%s"`, l.Who, l.Message) - p.b.Send(c, bot.Message, ch, msg) + p.b.Send(c, bot.Message, chTo, msg) }