From e614e1268abc689a13365204f83e2da8f436db50 Mon Sep 17 00:00:00 2001 From: Max Roeleveld Date: Tue, 21 Feb 2017 16:54:29 +0100 Subject: [PATCH] Add ChannelData to Cmd (#41) This stores a bit more info than just the channel name; in addition you get the protocol (irc, slack, telegram), server host, and whether the channel is a private chat or a public channel. --- bot.go | 11 ++++++----- cmd.go | 42 +++++++++++++++++++++++---------------- cmd_test.go | 30 ++++++++++++++-------------- help.go | 2 +- irc/irc.go | 15 ++++++++++---- parser.go | 5 +++-- parser_test.go | 47 +++++++++++++++++++++++--------------------- slack/slack.go | 43 +++++++++++++++++++++++++++++++++++----- telegram/telegram.go | 6 +++++- 9 files changed, 129 insertions(+), 72 deletions(-) diff --git a/bot.go b/bot.go index 28cc177..2aa404d 100644 --- a/bot.go +++ b/bot.go @@ -59,18 +59,19 @@ func (b *Bot) startPeriodicCommands() { } // MessageReceived must be called by the protocol upon receiving a message -func (b *Bot) MessageReceived(channel string, text string, sender *User) { +func (b *Bot) MessageReceived(channel *ChannelData, text string, sender *User) { command, err := parse(text, channel, sender) if err != nil { - b.handlers.Response(channel, err.Error(), sender) + b.handlers.Response(channel.Channel, err.Error(), sender) return } if command == nil { b.executePassiveCommands(&PassiveCmd{ - Raw: text, - Channel: channel, - User: sender, + Raw: text, + Channel: channel.Channel, + ChannelData: channel, + User: sender, }) return } diff --git a/cmd.go b/cmd.go index 804d828..16cef0a 100644 --- a/cmd.go +++ b/cmd.go @@ -8,20 +8,35 @@ import ( // Cmd holds the parsed user's input for easier handling of commands type Cmd struct { - Raw string // Raw is full string passed to the command - Channel string // Channel where the command was called - User *User // User who sent the message - Message string // Full string without the prefix - Command string // Command is the first argument passed to the bot - RawArgs string // Raw arguments after the command - Args []string // Arguments as array + Raw string // Raw is full string passed to the command + Channel string // Channel where the command was called + ChannelData *ChannelData // More info about the channel, including network + User *User // User who sent the message + Message string // Full string without the prefix + Command string // Command is the first argument passed to the bot + RawArgs string // Raw arguments after the command + Args []string // Arguments as array +} + +// ChannelData holds the improved channel info, which includes protocol and server +type ChannelData struct { + Protocol string // What protocol the message was sent on (irc, slack, telegram) + Server string // The server hostname the message was sent on + Channel string // The channel name the message appeared in + IsPrivate bool // Whether the channel is a group or private chat +} + +// URI gives back an URI-fied string containing protocol, server and channel. +func (c *ChannelData) URI() string { + return fmt.Sprintf("%s://%s/%s", c.Protocol, c.Server, c.Channel) } // PassiveCmd holds the information which will be passed to passive commands when receiving a message type PassiveCmd struct { - Raw string // Raw message sent to the channel - Channel string // Channel which the message was sent to - User *User // User who sent this message + Raw string // Raw message sent to the channel + Channel string // Channel which the message was sent to + ChannelData *ChannelData // Channel and network info + User *User // User who sent this message } // PeriodicConfig holds a cron specification for periodically notifying the configured channels @@ -47,13 +62,6 @@ type customCommand struct { ExampleArgs string } -type incomingMessage struct { - Channel string - Text string - User *User - BotCurrentNick string -} - // CmdResult is the result message of V2 commands type CmdResult struct { Channel string // The channel where the bot should send the message diff --git a/cmd_test.go b/cmd_test.go index 73f71ef..e32d164 100644 --- a/cmd_test.go +++ b/cmd_test.go @@ -101,13 +101,13 @@ func TestDisabledCommands(t *testing.T) { }) b.Disable([]string{"cmd"}) - b.MessageReceived("#go-bot", "!cmd", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!cmd", &User{Nick: "user"}) if len(replies) != 0 { t.Fatal("Should not execute disabled active commands") } b.Disable([]string{"passive"}) - b.MessageReceived("#go-bot", "regular message", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "regular message", &User{Nick: "user"}) if len(replies) != 0 { t.Fatal("Should not execute disabled passive commands") @@ -124,7 +124,7 @@ func TestMessageReceived(t *testing.T) { Convey("When the command is not registered", func() { Convey("It should not post to the channel", func() { - b.MessageReceived("#go-bot", "!not_a_cmd", &User{}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!not_a_cmd", &User{}) So(replies, ShouldBeEmpty) }) @@ -132,7 +132,7 @@ func TestMessageReceived(t *testing.T) { Convey("When the command arguments are invalid", func() { Convey("It should reply with an error message", func() { - b.MessageReceived("#go-bot", "!cmd \"invalid arg", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!cmd \"invalid arg", &User{Nick: "user"}) So(channel, ShouldEqual, "#go-bot") So(replies, ShouldHaveLength, 1) @@ -148,7 +148,7 @@ func TestMessageReceived(t *testing.T) { return "", cmdError }) - b.MessageReceived("#go-bot", "!cmd", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!cmd", &User{Nick: "user"}) So(channel, ShouldEqual, "#go-bot") So(replies, ShouldResemble, @@ -169,7 +169,7 @@ func TestMessageReceived(t *testing.T) { }) Convey("If it is called in the channel, reply on the channel", func() { - b.MessageReceived("#go-bot", "!cmd", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!cmd", &User{Nick: "user"}) So(channel, ShouldEqual, "#go-bot") So(replies, ShouldResemble, []string{expectedMsg}) @@ -177,13 +177,13 @@ func TestMessageReceived(t *testing.T) { Convey("If it is a private message, reply to the user", func() { user = &User{Nick: "go-bot"} - b.MessageReceived("go-bot", "!cmd", &User{Nick: "sender-nick"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!cmd", &User{Nick: "sender-nick"}) So(user.Nick, ShouldEqual, "sender-nick") }) Convey("When the command is help", func() { Convey("Display the available commands in the channel", func() { - b.MessageReceived("#go-bot", "!help", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!help", &User{Nick: "user"}) So(channel, ShouldEqual, "#go-bot") So(replies, ShouldResemble, []string{ @@ -193,7 +193,7 @@ func TestMessageReceived(t *testing.T) { }) Convey("If the command exists send a message to the channel", func() { - b.MessageReceived("#go-bot", "!help cmd", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!help cmd", &User{Nick: "user"}) So(channel, ShouldEqual, "#go-bot") So(replies, ShouldResemble, []string{ @@ -203,7 +203,7 @@ func TestMessageReceived(t *testing.T) { }) Convey("If the command does not exists, display the generic help", func() { - b.MessageReceived("#go-bot", "!help not_a_command", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!help not_a_command", &User{Nick: "user"}) So(channel, ShouldEqual, "#go-bot") So(replies, ShouldResemble, []string{ @@ -213,7 +213,7 @@ func TestMessageReceived(t *testing.T) { }) Convey("if the help arguments are invalid, reply with an error", func() { - b.MessageReceived("#go-bot", "!help cmd \"invalid arg", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!help cmd \"invalid arg", &User{Nick: "user"}) So(channel, ShouldEqual, "#go-bot") So(replies, ShouldHaveLength, 1) @@ -231,7 +231,7 @@ func TestMessageReceived(t *testing.T) { Message: "message"}, nil }) - b.MessageReceived("#go-bot", "!cmd", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!cmd", &User{Nick: "user"}) So(channel, ShouldEqual, "#channel") So(replies, ShouldResemble, []string{"message"}) @@ -244,7 +244,7 @@ func TestMessageReceived(t *testing.T) { Message: "message"}, nil }) - b.MessageReceived("#go-bot", "!cmd", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "!cmd", &User{Nick: "user"}) So(channel, ShouldEqual, "#go-bot") So(replies, ShouldResemble, []string{"message"}) @@ -269,7 +269,7 @@ func TestMessageReceived(t *testing.T) { RegisterPassiveCommand("errored", errored) Convey("If it is called in the channel, reply on the channel", func() { - b.MessageReceived("#go-bot", "test", &User{Nick: "user"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "test", &User{Nick: "user"}) So(channel, ShouldEqual, "#go-bot") So(len(replies), ShouldEqual, 2) @@ -279,7 +279,7 @@ func TestMessageReceived(t *testing.T) { Convey("If it is a private message, reply to the user", func() { user = &User{Nick: "go-bot"} - b.MessageReceived("go-bot", "test", &User{Nick: "sender-nick"}) + b.MessageReceived(&ChannelData{Channel: "#go-bot"}, "test", &User{Nick: "sender-nick"}) So(user.Nick, ShouldEqual, "sender-nick") So(len(replies), ShouldEqual, 2) diff --git a/help.go b/help.go index 1d2caf0..a02c2f6 100644 --- a/help.go +++ b/help.go @@ -14,7 +14,7 @@ const ( ) func (b *Bot) help(c *Cmd) { - cmd, _ := parse(CmdPrefix+c.RawArgs, c.Channel, c.User) + cmd, _ := parse(CmdPrefix+c.RawArgs, c.ChannelData, c.User) if cmd == nil { b.showAvailabeCommands(c.Channel, c.User) return diff --git a/irc/irc.go b/irc/irc.go index 22c6bc8..e82050a 100644 --- a/irc/irc.go +++ b/irc/irc.go @@ -37,10 +37,17 @@ func responseHandler(target string, message string, sender *bot.User) { } func onPRIVMSG(e *ircevent.Event) { - b.MessageReceived(e.Arguments[0], e.Message(), &bot.User{ - ID: e.Host, - Nick: e.Nick, - RealName: e.User}) + b.MessageReceived( + &bot.ChannelData{ + Protocol: "irc", + Server: ircConn.Server, + Channel: e.Arguments[0], + IsPrivate: e.Arguments[0] == ircConn.GetNick()}, + e.Message(), + &bot.User{ + ID: e.Host, + Nick: e.Nick, + RealName: e.User}) } func getServerName(server string) string { diff --git a/parser.go b/parser.go index 97be8f1..fb2004d 100644 --- a/parser.go +++ b/parser.go @@ -12,7 +12,7 @@ var ( re = regexp.MustCompile("\\s+") // Matches one or more spaces ) -func parse(s string, channel string, user *User) (*Cmd, error) { +func parse(s string, channel *ChannelData, user *User) (*Cmd, error) { c := &Cmd{Raw: s} s = strings.TrimSpace(s) @@ -20,7 +20,8 @@ func parse(s string, channel string, user *User) (*Cmd, error) { return nil, nil } - c.Channel = strings.TrimSpace(channel) + c.Channel = strings.TrimSpace(channel.Channel) + c.ChannelData = channel c.User = user // Trim the prefix and extra spaces diff --git a/parser_test.go b/parser_test.go index ca7bd79..531da57 100644 --- a/parser_test.go +++ b/parser_test.go @@ -5,8 +5,8 @@ import ( "testing" ) -func TestPaser(t *testing.T) { - channel := "#go-bot" +func TestParser(t *testing.T) { + channel := &ChannelData{Channel: "#go-bot"} user := &User{Nick: "user123"} cmdWithoutArgs := CmdPrefix + "cmd" cmdWithArgs := CmdPrefix + "cmd arg1 arg2 " @@ -20,29 +20,32 @@ func TestPaser(t *testing.T) { {"!", nil}, {"regular message", nil}, {cmdWithoutArgs, &Cmd{ - Raw: cmdWithoutArgs, - Command: "cmd", - Channel: channel, - User: user, - Message: "cmd", + Raw: cmdWithoutArgs, + Command: "cmd", + Channel: channel.Channel, + ChannelData: channel, + User: user, + Message: "cmd", }}, {cmdWithArgs, &Cmd{ - Raw: cmdWithArgs, - Command: "cmd", - Channel: channel, - User: user, - Message: "cmd arg1 arg2", - RawArgs: "arg1 arg2", - Args: []string{"arg1", "arg2"}, + Raw: cmdWithArgs, + Command: "cmd", + Channel: channel.Channel, + ChannelData: channel, + User: user, + Message: "cmd arg1 arg2", + RawArgs: "arg1 arg2", + Args: []string{"arg1", "arg2"}, }}, {cmdWithQuotes, &Cmd{ - Raw: cmdWithQuotes, - Command: "cmd", - Channel: channel, - User: user, - Message: "cmd \"arg1 arg2\"", - RawArgs: "\"arg1 arg2\"", - Args: []string{"arg1 arg2"}, + Raw: cmdWithQuotes, + Command: "cmd", + Channel: channel.Channel, + ChannelData: channel, + User: user, + Message: "cmd \"arg1 arg2\"", + RawArgs: "\"arg1 arg2\"", + Args: []string{"arg1 arg2"}, }}, } @@ -55,7 +58,7 @@ func TestPaser(t *testing.T) { } func TestInvalidArguments(t *testing.T) { - cmd, err := parse("!cmd Invalid \"arg", "#go-bot", &User{Nick: "user123"}) + cmd, err := parse("!cmd Invalid \"arg", &ChannelData{Channel: "#go-bot"}, &User{Nick: "user123"}) if err == nil { t.Error("Expected error, got nil") } diff --git a/slack/slack.go b/slack/slack.go index 9e2ba6f..d4e4bde 100644 --- a/slack/slack.go +++ b/slack/slack.go @@ -9,11 +9,13 @@ import ( ) var ( - rtm *slack.RTM - api *slack.Client + rtm *slack.RTM + api *slack.Client + teaminfo *slack.TeamInfo - params = slack.PostMessageParameters{AsUser: true} - botUserID = "" + channelList = map[string]slack.Channel{} + params = slack.PostMessageParameters{AsUser: true} + botUserID = "" ) func responseHandler(target string, message string, sender *bot.User) { @@ -42,6 +44,17 @@ func readBotInfo(api *slack.Client) { botUserID = info.UserID } +func readChannelData(api *slack.Client) { + channels, err := api.GetChannels(true) + if err != nil { + fmt.Printf("Error getting Channels: %s\n", err) + return + } + for _, channel := range channels { + channelList[channel.ID] = channel + } +} + func ownMessage(UserID string) bool { return botUserID == UserID } @@ -50,6 +63,7 @@ func ownMessage(UserID string) bool { func Run(token string) { api = slack.New(token) rtm = api.NewRTM() + teaminfo, _ = api.GetTeamInfo() b := bot.New(&bot.Handlers{ Response: responseHandler, @@ -65,9 +79,28 @@ Loop: switch ev := msg.Data.(type) { case *slack.HelloEvent: readBotInfo(api) + readChannelData(api) + case *slack.ChannelCreatedEvent: + readChannelData(api) + case *slack.ChannelRenameEvent: + readChannelData(api) + case *slack.MessageEvent: if !ev.Hidden && !ownMessage(ev.User) { - b.MessageReceived(ev.Channel, ev.Text, extractUser(ev.User)) + C := channelList[ev.Channel] + var channel = ev.Channel + if C.IsChannel { + channel = fmt.Sprintf("#%s", C.Name) + } + b.MessageReceived( + &bot.ChannelData{ + Protocol: "slack", + Server: teaminfo.Domain, + Channel: channel, + IsPrivate: !C.IsChannel, + }, + ev.Text, + extractUser(ev.User)) } case *slack.RTMError: diff --git a/telegram/telegram.go b/telegram/telegram.go index 5283fe2..8b59465 100644 --- a/telegram/telegram.go +++ b/telegram/telegram.go @@ -52,7 +52,11 @@ func Run(token string, debug bool) { b.Disable([]string{"url"}) for update := range updates { - target := strconv.FormatInt(update.Message.Chat.ID, 10) + target := &bot.ChannelData{ + Protocol: "telegram", + Server: "telegram", + Channel: strconv.FormatInt(update.Message.Chat.ID, 10), + IsPrivate: update.Message.Chat.IsPrivate()} name := []string{update.Message.From.FirstName, update.Message.From.LastName} b.MessageReceived(target, update.Message.Text, &bot.User{