// moduleExited is called when a module process has exited. If an error was returned when exiting // it will be logged. The output (Stdout and Stderr) is also logged. If the module exists in the // handler it will be removed. The module is also unregistered from the bot. func (b *Bot) moduleExited(name string, result *Result) { b.Mu.Lock() defer b.Mu.Unlock() if result.Err != nil { rlog.Error("Bot", name+" [Module Process] has exited with: "+result.Err.Error()) } else { rlog.Info("Bot", "Module "+name+" [Module Process] has exited") } if result.Output != "" { rlog.Info(name, "Process output:"+"\n ----\n"+result.Output+"\n ----\n") } if b.Handler.ModuleExists(name) { // If the module process is dead we can rest assured signalling it to clean up will fail // so we won't even try. It's the sanest thing to do! b.Handler.RemoveModule(ModuleName(name)) } if !b.Modules[name].Registered { rlog.Error("Bot", name+" [Module Client] did not manage to register") } else { b.Modules[name].Registered = false rlog.Info("Bot", name+" [Module Client] has been unregistered from the bot") } }
// DefaultConnectWithMsg is similar to DefaultConnect, except a user may pass a pre-connect and // post-connect message. func (b *Bot) DefaultConnectWithMsg(pre string, post string) { if pre != "" { rlog.Info("Bot", pre) } b.Connect() if post != "" { rlog.Info("Bot", post) } b.Listen() }
// LoadModules starts the bot's rpc master server and then calls moduleRun() on all modules. func (b *Bot) loadModules() { b.startRPCServer() for name, module := range b.Modules { if _, ok := module.Options["noload"]; ok { rlog.Info("Bot", "Module "+name+" has option noload, not loading") continue } go func(name string, pm *ProcessManager) { result := pm.Start(b.ListenPort) b.moduleExited(name, result) }(name, module.PM) rlog.Info("Bot", "Module "+name+" loaded") } }
// EnableModules goes through every module list found in the config and sets them up appropriately. func (b *Bot) EnableModules() { rlog.Info("Bot", "Enabling Modules...") b.Handler.AddRemoveCallback(func(name ModuleName) { b.Modules[strings.ToLower(string(name))].Registered = false }) for modtype, modules := range b.Config.Module.Modules { for name, value := range modules { route, optionsArray := b.Parser.ParseModuleValue(value) optionsMap := make(map[string]bool) for _, opt := range optionsArray { optionsMap[opt] = true } if route == "." { route = DefaultModulesRoute + modtype } b.Modules[strings.ToLower(name)] = NewModule(name, route, optionsMap, modtype) rlog.Debug("Bot", "Module "+name+" Created") } } b.loadModules() }
// JoinChannel takes a JoinRequest. It will add the channel to join to the ToJoinChs map so that // when the bot receives a JOIN reply from the IRC server, it can verify whether it joined a // channel. func (b BotAPI) JoinChannel(jr JoinRequest, result *string) error { b.bot.ToJoinChs[strings.ToLower(jr.Channel)] = jr.Caller if jr.Password != "" { b.bot.Send(irc.JOIN, jr.Channel, jr.Password) } else { rlog.Info("BAPI", "Joining "+jr.Channel) b.bot.Send(irc.JOIN, jr.Channel) } return nil }
// startRPCServer registers the master consumer for plugins. The master consumer allows plugins to // communicate with the bot, allowing access to connected channels, users and registered modules. // Conventionally, it uses a json codec to serve. func (b *Bot) startRPCServer() { rpc.RegisterName("Master", BotAPI{b}) master, err := net.Listen("tcp", ":0") b.ListenPort = strconv.Itoa(master.Addr().(*net.TCPAddr).Port) rlog.Info("Bot", "Listening on port: "+b.ListenPort) if err != nil { rlog.Error("Bot", err.Error()) } // Start accepting connections go func() { for { conn, _ := master.Accept() go rpc.ServeCodec(RpcCodecServer(conn)) } }() }
// Join will create a new channel if the bot is the one joining. Otherwise it updates a channel's // user list func (l *Listeners) Join(msg *irc.Message) { l.bot.Mu.Lock() defer l.bot.Mu.Unlock() who := msg.Prefix.Name where := msg.Trailing if who == l.bot.GetNick() { rlog.Info("Bot", "Joined: "+where) caller, ok := l.bot.ToJoinChs[strings.ToLower(where)] if ok { l.bot.Say(caller, "Joined "+where) } channel := rbot.NewChannel(where) l.bot.Channels[strings.ToLower(where)] = channel return } channel := l.bot.Channels[strings.ToLower(where)] channel.Users[who] = "" }