func (ei *EndpointIRC) message(c *irc.Conn, l *irc.Line) { var messageTarget MessageTarget if l.Public() { messageTarget = ei.GetChannel(l.Target()) } else { messageTarget = ei.GetUser(l.Target()) } ei.handler(l.Text(), ei.GetUser(l.Nick), l.Target(), messageTarget) }
func (self *ModManager) run(event string, line *irc.Line) { self.mut.RLock() defer self.mut.RUnlock() for _, mod := range self.modules { // Module should check if enabled, not handlers go mod.Handle(module.Event(event), line.Text(), line) } }
func (self *Module) handleRegexp(eventMode Event, trigger string, line *irc.Line) { self.reMut.RLock() defer self.reMut.RUnlock() for _, reM := range self.reTriggers[eventMode] { if reM.trigger.MatchString(trigger) { go reM.fn(line.Copy()) } } }
func (self *Module) handleString(eventMode Event, trigger string, line *irc.Line) { trigger = strings.ToLower(trigger) evT := eventTrigger{eventMode, trigger} self.stMut.RLock() defer self.stMut.RUnlock() for _, fn := range self.stTriggers[evT] { go fn(line.Copy()) } }
// Implement client.Handler so commandSet can Handle things directly. func (cs *commandSet) Handle(conn *client.Conn, line *client.Line) { // This is a dirty hack to treat factoid additions as a special // case, since they may begin with command string prefixes. ctx := context(conn, line) if ctx == nil || util.IsFactoidAddition(line.Text()) { return } if r, ln := cs.match(ctx.Text()); ctx.Addressed && r != nil { // Cut command off, trim and compress spaces. ctx.Args[1] = strings.Join(strings.Fields(ctx.Args[1][ln:]), " ") r.Run(ctx) } }
func handle_command(conn *irc.Conn, line *irc.Line, args []string) { command := args[0][len(RC.CmdPrefix):] args = args[1:] st := conn.StateTracker() authed := RC.Authed[line.Nick] if line.Nick == RC.Owner && authed { // Owner-only commands if command == "mock" { for _, n := range args { _, is_on := st.IsOn(RC.Channel, n) if is_on { RC.Mocking[n] = true conn.Privmsg(RC.Channel, "Now mocking "+n) } } } else if command == "unmock" { for _, n := range args { if RC.Mocking[n] { RC.Mocking[n] = false conn.Privmsg(RC.Channel, "No longer mocking "+n) } } } else if command == "shorten" { fmt.Println("unimplemented") } else if command == "snoop" { fmt.Println("unimplemented") } } else if (RC.Ops[line.Nick] || line.Nick == RC.Owner) && authed { // Ops-level commands if command == "poop" { conn.Privmsg(RC.Channel, "poop") } } else if command == "identify" && !line.Public() { fmt.Println("User " + line.Nick + " attempting to auth...") h := sha256.New() h.Write([]byte(args[0])) pwd_hash := hex.EncodeToString(h.Sum(nil)) if (line.Nick == RC.Owner || RC.Ops[line.Nick]) && RC.Password == pwd_hash { fmt.Println("Auth succeeded for " + line.Nick) RC.Authed[line.Nick] = true conn.Privmsg(line.Nick, "You're authenticated now.") } else { fmt.Println("WARNING: Auth failed for " + line.Nick + "!") conn.Privmsg(line.Nick, "You f****d up.") } } }
func (ib *IrcBot) parsePacket(conn *irc.Conn, line *irc.Line) *models.Packet { result := ib.regex.FindStringSubmatch(line.Text()) if result == nil { return nil } fileName := cleanFileName(result[3]) packet := models.NewPacket(result[1], result[2], fileName, line.Nick, line.Target(), ib.server.Name, line.Time) //save packet if packet != nil { ib.dataService.SavePacket(packet) } return packet }
// Handles triggers if module is enabled and user/chan is allowed. This is mainly // exported for use by library and should not have to be called by the user func (self *Module) Handle(eventMode Event, trigger string, line *irc.Line) { // Filtered by: denyUser, allowUser, denyChan, allowChan if !self.Enabled() || self.InDenyed(line.Nick) || // Empty allowUser list => allow all (self.LenAllowed(UC_User) != 0 && !self.InAllowed(line.Nick)) || self.InDenyed(line.Target()) || // Empty denyChan list => allow all (self.LenAllowed(UC_Chan) != 0 && !self.InAllowed(line.Target())) { return } eventMode = Event(strings.ToUpper(string(eventMode))) go self.handleString(eventMode, trigger, line) go self.handleRegexp(eventMode, trigger, line) }
func context(conn *client.Conn, line *client.Line) *Context { ctx := &Context{conn: conn, Line: line.Copy(), rws: bot.rewriters} // This is a bit of a dirty hack; context() returns nil to ignore a line. // TODO(fluffle): Ignores based on masks (or more likely regex). if ctx.Nick != "" && conf.Ns(ignoreNs).String(strings.ToLower(ctx.Nick)) != "" { return nil } if ctx.Cmd != client.PRIVMSG { return ctx } ctx.Args[1], ctx.Addressed = util.RemovePrefixedNick( strings.TrimSpace(ctx.Args[1]), ctx.Me()) // If we're being talked to in private, line.Args[0] will contain our Nick. // We should consider this as "addressing" us, and set Addressed = true if ctx.Args[0] == ctx.Me() { ctx.Addressed = true } return ctx }
func transformLine(line *client.Line) *base.Line { // We want line.Args[1] to contain the (possibly) stripped version of itself // but modifying the pointer will result in other goroutines seeing the // change, so we need to copy line for our own edification. nl := &base.Line{Line: line.Copy()} if nl.Cmd != "PRIVMSG" { return nl } nl.Args[1], nl.Addressed = util.RemovePrefixedNick( strings.TrimSpace(line.Args[1]), irc.Me.Nick) // If we're being talked to in private, line.Args[0] will contain our Nick. // To ensure the replies go to the right place (without performing this // check everywhere) test for this and set line.Args[0] == line.Nick. // We should consider this as "addressing" us too, and set Addressed = true if nl.Args[0] == irc.Me.Nick { nl.Args[0] = nl.Nick nl.Addressed = true } return nl }
// Handle privmsgs func handle_privmsg(conn *irc.Conn, line *irc.Line) { text := line.Text() args := strings.Split(text, " ") //var url_regex = regexp.MustCompile(`\Ahttps?://([[:alnum:]][a-zA-Z0-9-]{1,61}[[:alnum:]]\.?){2,3}((%[0-9A-Fa-f]{2}|[-_.!~*';/?#:@&=+$,A-Za-z0-9])+)?\z`) var url_regex = regexp.MustCompile(`\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))`) // handle a prefixed command if strings.Index(text, RC.CmdPrefix) == 0 { handle_command(conn, line, args) } else if len(url_regex.FindString(text)) > 0 { s := GetScanner() s.Handle(conn, line, text) } // Handle mocking if RC.Mocking[line.Nick] { SendMessage(conn, "Hey, everybody! "+line.Nick+" said something!", nil) SendMessage(conn, line.Nick+": "+line.Text(), nil) SendMessage(conn, "Great job, "+line.Nick+"! ╭(ᐛ)و", nil) } }
// Do some standard processing on incoming lines and dispatch a bot_privmsg func bot_privmsg(irc *client.Conn, line *client.Line) { bot := getState(irc) l, p := util.RemovePrefixedNick(strings.TrimSpace(line.Args[1]), irc.Me.Nick) // We want line.Args[1] to contain the (possibly) stripped version of itself // but modifying the pointer will result in other goroutines seeing the // change, so we need to copy line for our own edification. nl := &base.Line{Line: *line.Copy()} nl.Args[1] = l nl.Addressed = p // If we're being talked to in private, line.Args[0] will contain our Nick. // To ensure the replies go to the right place (without performing this // check everywhere) test for this and set line.Args[0] == line.Nick. // We should consider this as "addressing" us too, and set Addressed = true if nl.Args[0] == irc.Me.Nick { nl.Args[0] = nl.Nick nl.Addressed = true } bot.Dispatch("bot_privmsg", nl) }
func (ib *IrcBot) handleNotice(conn *irc.Conn, line *irc.Line) { log.Printf("[NOTICE] %v", line.Text()) ib.logToConsole("[NOTICE] " + line.Text()) }
func (ib *IrcBot) log372(conn *irc.Conn, line *irc.Line) { ib.logToConsole(line.Text()) }
func (plugin *Plugin) OnJoin(conn *client.Conn, line *client.Line) { logging.Info("vpnbot.Plugin: %v joined %v", line.Src, line.Target()) if lastCheck, ok := plugin.lastCheckNicks[line.Nick]; ok && time.Now().Sub(lastCheck) < 15*time.Minute { // There is a check in progress or another one has been done earlier logging.Debug("vpnbot.Plugin: Not checking %v, last check was %v", line.Nick, humanize.Time(plugin.lastCheckNicks[line.Nick])) return } logging.Debug("vpnbot.Plugin: Checking %v...", line.Nick) plugin.lastCheckNicks[line.Nick] = time.Now() // Is this me? if line.Nick == conn.Me().Nick { logging.Debug("vpnbot.Plugin: %v is actually me, skipping.", line.Nick) return } // Nickname == Ident? (9 chars cut) if !strings.HasPrefix(nonWordCharsRegex.ReplaceAllString(line.Nick, ""), strings.TrimLeft(line.Ident, "~")) { logging.Debug("vpnbot.Plugin: %v's nick doesn't match the ident, skipping.", line.Nick) return } // Hostname is masked RDNS vhost/IP? // TODO - Use regex to avoid banning due to similar vhosts if !maskedAddrRegex.MatchString(line.Host) { // Detected custom vHost logging.Debug("vpnbot.Plugin: %v has a custom vhost, skipping.", line.Nick) return } go func() { botNick := line.Nick nobotActivityChan := make(chan string) defer plugin.bot.HandleFunc("privmsg", func(conn *client.Conn, line *client.Line) { if line.Nick == botNick { nobotActivityChan <- "User sent a message" } }).Remove() defer plugin.bot.HandleFunc("noticed", func(conn *client.Conn, line *client.Line) { if line.Nick == botNick { nobotActivityChan <- "User sent a notice" } }).Remove() defer plugin.bot.HandleFunc("part", func(conn *client.Conn, line *client.Line) { if line.Nick == botNick { nobotActivityChan <- "User left" } }).Remove() defer plugin.bot.HandleFunc("part", func(conn *client.Conn, line *client.Line) { if line.Nick == botNick { } if len(line.Args) > 0 { switch line.Args[0] { case "Excess flood": // If this was an excess flood, consider it spam that should // be good to ban anyways nobotActivityChan <- "Excess flood, banning early" banmask := fmt.Sprintf("%v!%v@%v", "*", "*", line.Host) // TODO - Ramp up/down the duration with increasing/decreasing activity of the bots plugin.banGlobal(plugin.generateBan(line.Nick, banmask, "Instant excess flood", 2*24*time.Hour)) default: nobotActivityChan <- "User quit normally" } } }).Remove() // Give nobotActivityChan some time to prove this is not a bot select { case reason := <-nobotActivityChan: logging.Info("vpnbot.Plugin: %v, skipping.", reason) return case <-time.After(1 * time.Second): } // Get WHOIS info info, err := plugin.whois.WhoIs(line.Nick) if err != nil && err != whois.ErrNoSuchNick { logging.Warn("vpnbot.Plugin: Can't get WHOIS info for %v, skipping: %v", line.Nick, err.Error()) return } // Not an oper? if info.IsOperator { logging.Debug("vpnbot.Plugin: %v is operator, skipping.", line.Nick) return } // Not away if info.IsAway { logging.Debug("vpnbot.Plugin: %v is away, skipping.", line.Nick) return } // Realname == Nickname? if info.Realname != line.Nick { logging.Debug( "vpnbot.Plugin: %v's nick doesn't match the realname, skipping.", line.Nick) return } // Count of channels at least 48 if len(info.Channels) < 48 { logging.Debug( "vpnbot.Plugin: %v is not in a high amount of channels, skipping.", line.Nick) return } // Not halfop, op, aop or owner in any channel for _, prefix := range info.Channels { if prefix == '%' || prefix == '@' || prefix == '&' || prefix == '~' { logging.Debug( "vpnbot.Plugin: %v is opped in a channel, skipping.", line.Nick) return } } // Give nobotActivityChan some time to prove this is not a bot select { case reason := <-nobotActivityChan: logging.Info("vpnbot.Plugin: %v, skipping.", reason) return case <-time.After(250 * time.Millisecond): } // More expensive tests below... // Make sure we deal with an unregistered client status := plugin.nickserv.Status(line.Nick)[line.Nick] if status.Error != nil { logging.Warn("vpnbot.Plugin: Can't get auth status for %v, skipping: %v", line.Nick, status.Error) return } if status.Level >= nickserv.Status_IdentifiedViaPassword { logging.Debug("vpnbot.Plugin: %v is identified, skipping.", line.Nick) return } // Give nobotActivityChan some time to prove this is not a bot select { case reason := <-nobotActivityChan: logging.Info("vpnbot.Plugin: %v, skipping.", reason) return case <-time.After(250 * time.Millisecond): } // This is a bot we need to ban! banmask := fmt.Sprintf("%v!%v@%v", "*", "*", line.Host) // TODO - Ramp up/down the duration with increasing/decreasing activity of the bots plugin.banGlobal(plugin.generateBan(line.Nick, banmask, "Known pattern of ban-evading/logging bot", 2*24*time.Hour)) }() }