func (plugin *Plugin) unbanGlobal(hostmask string) { modesNative, ok := plugin.isupport.Supports().Modes() if !ok { modesNative = 1 } modeBuf := util.NewModeChangeBuffer(plugin.bot.Conn(), modesNative) for _, channel := range plugin.bot.Channels() { // -b will get sent back by the server and then picked up by our mode // changing listener that we hooked in the New() method. modeBuf.Mode(channel, "-b", hostmask) } modeBuf.Flush() }
// Sets multiple bans in a target channel. // // Returns an error slice of the same amount of elements as bans // passed in, each error corresponding to the respective ban by index. // // All errors will be ErrNotAChannel if the target is not a valid channel. func (p *Plugin) Ban(target string, bans ...TemporaryBan) (retval []error) { retval = make([]error, len(bans)) if ok, _, _ := p.isupport.IsChannel(target); !ok { for index, _ := range retval { retval[index] = ErrNotAChannel } return // not a channel } modesNative, ok := p.isupport.Supports().Modes() if !ok { modesNative = 1 } modeBuf := util.NewModeChangeBuffer(p.bot.Conn(), modesNative) // NOTE - We assume that responses to bans get returned in the same order // as we send out the ban requests... this may very well go wrong! index := 0 banSetChans := []chan error{} banSetMutex := &sync.Mutex{} // Populate banSetChans with channels to send to // TODO - There probably is a more efficient way to do this for _, _ = range bans { banSetChans = append(banSetChans, make(chan error)) } // TODO - This will very well fail on multiple async calls to this method // on the same target while both are waiting for replies as the index will // get bigger than the length of bans. // We also can not use a map by hostmask as some of these replies are far // too vague and don't provide anything but our nickname and the error // message. // Sometimes I really hate the IRC protocol because of things like that... defer p.bot.HandleFunc("482", // ERR_CHANOPRIVSNEEDED func(conn *client.Conn, line *client.Line) { if line.Args[0] != conn.Me().Nick || line.Args[1] != target { return } banSetMutex.Lock() defer banSetMutex.Unlock() for i := index; i < int(math.Min(float64(len(banSetChans)), float64(uint64(index)+modesNative))); i++ { banSetChans[index] <- errors.New("Missing channel operator privileges") } // This error message counts for all bans sent in the same request // line. index += int(modesNative) }).Remove() defer p.bot.HandleFunc("478", // ERR_BANLISTFULL func(conn *client.Conn, line *client.Line) { if line.Args[0] != conn.Me().Nick || line.Args[1] != target { return } banSetMutex.Lock() defer banSetMutex.Unlock() for i := index; i < int(math.Min(float64(len(banSetChans)), float64(uint64(index)+modesNative))); i++ { banSetChans[index] <- errors.New("The ban list is full") } // This error message counts for all bans sent in the same request // line. index += int(modesNative) }).Remove() defer p.mode.HandleFunc("+b", func(e *mode.ModeChangeEvent) { if !strings.EqualFold(e.Target, target) { return } banSetMutex.Lock() defer banSetMutex.Unlock() banSetChans[index] <- nil index++ }).Remove() // -b+b will definitely trigger a MODE +b response if the ban can be set for _, ban := range bans { modeBuf.Mode(target, "-b", ban.Hostmask) modeBuf.Mode(target, "+b", ban.Hostmask) } modeBuf.Flush() // Now wait for the responses anyBanAppliedCorrectly := false for i, ch := range banSetChans { select { case err := <-ch: close(ch) retval[i] = err if err == nil { p.ensureTemporaryBanManager(target).Add(bans[i]) anyBanAppliedCorrectly = true } } } if anyBanAppliedCorrectly { p.syncBans(target) } return }