From 4efaa315479b59919e8fe1ed04e5adf5284bf2a0 Mon Sep 17 00:00:00 2001 From: "Bruno E. O. Meneguele" Date: Sat, 26 Aug 2017 21:35:43 -0300 Subject: [PATCH 1/2] command: support on callbacks to protocol events-only Every message or event that has a callback registered passes its information through a generic callback interface, which doesn't care about the source event. It always waits a message to be present, and because of that it's impossible to register 'events-only' commands. New field 'EventCode' on 'Message' structure allows callbacks to events that doesn't require explicit message text, that just the name or code of that event would be enough. --- bot.go | 11 +++++++++++ cmd.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/bot.go b/bot.go index c33cd72..318faf1 100644 --- a/bot.go +++ b/bot.go @@ -5,6 +5,7 @@ import ( "log" "math/rand" "time" + "strings" "github.com/robfig/cron" ) @@ -62,6 +63,16 @@ func (b *Bot) startPeriodicCommands() { // MessageReceived must be called by the protocol upon receiving a message func (b *Bot) MessageReceived(channel *ChannelData, message *Message, sender *User) { + if message.Text == "" { + b.ExecuteEventCommands(&EventCmd{ + EventCode: message.EventCode, + Channel: strings.TrimSpace(channel.Channel), + ChannelData: channel, + User: sender, + }) + return + } + command, err := parse(message.Text, channel, sender) if err != nil { b.handlers.Response(channel.Channel, err.Error(), sender) diff --git a/cmd.go b/cmd.go index 418c1f6..b66a98b 100644 --- a/cmd.go +++ b/cmd.go @@ -35,6 +35,7 @@ func (c *ChannelData) URI() string { // Message holds the message info - for IRC and Slack networks, this can include whether the message was an action. type Message struct { Text string // The actual content of this Message + EventCode string // Event that triggered this message to happen (protocol specific) IsAction bool // True if this was a '/me does something' message } @@ -47,6 +48,13 @@ type PassiveCmd struct { User *User // User who sent this message } +type EventCmd struct { + EventCode string // Which event code has actually occured + Channel string // Channel wich 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 type PeriodicConfig struct { CronSpec string // CronSpec that schedules some function @@ -91,11 +99,13 @@ const ( type passiveCmdFunc func(cmd *PassiveCmd) (string, error) type activeCmdFuncV1 func(cmd *Cmd) (string, error) type activeCmdFuncV2 func(cmd *Cmd) (CmdResult, error) +type eventCmdFunc func(cmd *EventCmd) (string, error) var ( commands = make(map[string]*customCommand) passiveCommands = make(map[string]passiveCmdFunc) periodicCommands = make(map[string]PeriodicConfig) + eventCommands = make(map[string]eventCmdFunc) ) // RegisterCommand adds a new command to the bot. @@ -135,6 +145,14 @@ func RegisterPassiveCommand(command string, cmdFunc func(cmd *PassiveCmd) (strin passiveCommands[command] = cmdFunc } +// RegisterEventCommand adds a new passive command to the bot. +// The command should be registered in the Init() func of your package +// command: String which the user will use to add some behavior in some irc event +// cmdFunc: Function which will be executed once the irc event occurs +func RegisterEventCommand(command string, cmdFunc eventCmdFunc) { + eventCommands[command] = cmdFunc +} + // RegisterPeriodicCommand adds a command that is run periodically. // The command should be registered in the Init() func of your package // config: PeriodicConfig which specify CronSpec and a channel list @@ -178,6 +196,34 @@ func (b *Bot) executePassiveCommands(cmd *PassiveCmd) { wg.Wait() } +func (b *Bot) ExecuteEventCommands(cmd *EventCmd) { + var wg sync.WaitGroup + mutex := &sync.Mutex{} + + for k, v := range eventCommands { + if b.isDisabled(k) { + continue + } + + cmdFunc := v + wg.Add(1) + + go func() { + defer wg.Done() + + result, err := cmdFunc(cmd) + if err != nil { + log.Println(err) + } else { + mutex.Lock() + b.handlers.Response(cmd.Channel, result, cmd.User) + mutex.Unlock() + } + }() + } + wg.Wait() +} + func (b *Bot) isDisabled(cmd string) bool { for _, c := range b.disabledCmds { if c == cmd { From a7bee3152f0d5c99c46e354dbd95ba271557f1d7 Mon Sep 17 00:00:00 2001 From: "Bruno E. O. Meneguele" Date: Sat, 26 Aug 2017 21:42:11 -0300 Subject: [PATCH 2/2] command: add support for commands over JOIN event Add support for commands that relies on JOIN event from users in the channel. This support to JOIN was made through the new features on callbacks' interface. For now, just JOIN event can be monitored by commands. --- irc/irc.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/irc/irc.go b/irc/irc.go index 50e097c..ddb3e7e 100644 --- a/irc/irc.go +++ b/irc/irc.go @@ -74,6 +74,24 @@ func onCTCPACTION(e *ircevent.Event) { }) } +func onJOIN(e *ircevent.Event) { + b.MessageReceived( + &bot.ChannelData{ + Protocol: "irc", + Server: ircConn.Server, + Channel: e.Arguments[0], + IsPrivate: false, + }, + &bot.Message{ + EventCode: e.Code, + }, + &bot.User{ + ID: e.Host, + Nick: e.Nick, + RealName: e.User, + }) +} + func getServerName(server string) string { separatorIndex := strings.LastIndex(server, ":") if separatorIndex != -1 { @@ -108,6 +126,7 @@ func Run(c *Config) { ircConn.AddCallback("001", onWelcome) ircConn.AddCallback("PRIVMSG", onPRIVMSG) ircConn.AddCallback("CTCP_ACTION", onCTCPACTION) + ircConn.AddCallback("JOIN", onJOIN) err := ircConn.Connect(c.Server) if err != nil {