func main() { // If the constants are valid, this program cannot crash. Period. defer func() { if err := recover(); err != nil { msg := fmt.Sprintf("Recovered from nasty error in main: %v\n", err) // ircMsg(msg) fmt.Print(msg) } }() // Connect to IRC conn = ircSetup() defer conn.Close() // Anything passed to the `irc` channel (get it?) is echoed into // IRC_CHANNEL go func() { for { ircMsg(<-irc) } }() // // Main loop // read_buf := make([]byte, 512) for { // n bytes read n, err := conn.Read(read_buf) checkError("conn.Read", err) rawData := string(read_buf[:n]) rawData = strings.TrimRight(rawData, " \t\r\n") // Remove trailing whitespace fmt.Printf("%v\n", rawData) // // Respond to PING // if strings.HasPrefix(rawData, "PING") { rawIrcMsg("PONG " + rawData) continue } // // Parse nick, msg // // Avoids ~global var risk by resetting these to "" each loop var msg, nick = "", "" // Parse nick when safe to do so // TODO: Look at IRC spec; not sure this is guaranteed to work if fun.ContainsAnyStrings(rawData, "PRIVMSG", "MODE", "JOIN", "KICK") { // structure of `rawData` == :nick!host PRIVMSG #channel :msg // nick == everything after first char, before first ! nick = strings.SplitN(rawData[1:], "!", 2)[0] fmt.Printf("Nick: '%v'\n", nick) } // Parse msg when safe to do so // TODO: Make this much more precise if fun.ContainsAnyStrings(rawData, "PRIVMSG", "KICK") { // msg == everything after second : msg = strings.SplitN(rawData, ":", 3)[2] fmt.Printf("Message: '%v'\n", msg) } // Thank user when given OP... then seek revenge // TODO: Use regex to parse `rawData` if strings.Contains(rawData, "MODE "+IRC_CHANNEL+" +o "+BOT_NICK) { irc <- nick + ": thanks :-)" for _, user := range revenge { rawIrcMsg("KICK " + IRC_CHANNEL + " " + user + " :" + REVENGE_MSG) } revenge = []string{} } // Seek revenge on those who remove bot's OP // TODO: Use regex to parse `rawData` if strings.Contains(rawData, "MODE "+IRC_CHANNEL+" -o "+BOT_NICK) { irc <- ":-(" revenge = append(revenge, nick) } // // Re-join if kicked // if fun.ContainsAllStrings(rawData, "KICK", BOT_NICK) { rawIrcMsg("JOIN " + IRC_CHANNEL) revenge = append(revenge, nick) } // // Respond to queries for word definitions using DuckDuckGo // if strings.HasPrefix(msg, "!define ") { query := msg[1:] resp, err := ddg.ZeroClick(query) if err != nil { irc <- fmt.Sprintf("Error querying DuckDuckGo: %v\n", err) } else { irc <- fmt.Sprintf("%s: %s\n", query, resp.Abstract) } } // // Response to DuckDuckGo queries // if strings.HasPrefix(msg, "!ddg ") { resp, err := ddg.ZeroClick(msg[len("!ddg "):]) if err != nil { irc <- fmt.Sprintf("Error querying DuckDuckGo: %v\n", err) } else { irc <- fmt.Sprintf("DuckDuckGo: %s\n", resp.Abstract) } } // // ADD YOUR CODE (or function calls) HERE // } }
func main() { // If the constants are valid, this program cannot crash. Period. defer func() { if err := recover(); err != nil { msg := fmt.Sprintf("Recovered from nasty error in main: %v\n", err) // ircMsg(msg) fmt.Print(msg) } }() // Connect to IRC conn = ircSetup() defer conn.Close() // Listen for repo names on port GIT_PORT, then echo info // from latest commit into IRC_CHANNEL. Currently triggered by // post-receive git hooks. go gitListener() // Listen for (WebHook-powered) JSON POSTs from GitHub to port // WEBHOOK_PORT go webhookListener() // Anything passed to the `irc` channel (get it?) is echoed into // IRC_CHANNEL go func() { for { ircMsg(<-irc) } }() // // Main loop // read_buf := make([]byte, 512) for { // n bytes read n, err := conn.Read(read_buf) checkError("conn.Read", err) data := string(read_buf[:n]) data = strings.TrimRight(data, "\t\r\n") // Remove trailing whitespace fmt.Printf("%v\n", data) // // Respond to PING // if strings.HasPrefix(data, "PING") { rawIrcMsg("PONG " + data) } // // Parse nick, msg // // Avoids ~global var risk by resetting these to "" each loop var msg, nick string = "", "" // Parse nick when safe to do so // TODO: Look at IRC spec; not sure this is guaranteed to work if fun.ContainsAnyStrings(data, "PRIVMSG", "MODE", "JOIN", "KICK") { // structure of `data` == :nick!host PRIVMSG #channel :msg // nick == everything after first char, before first ! nick = strings.SplitN(data[1:], "!", 2)[0] fmt.Printf("Nick: '%v'\n", nick) } // Parse msg when safe to do so // TODO: Make this much more precise if fun.ContainsAnyStrings(data, "PRIVMSG", "KICK") { // msg == everything after second : msg = strings.SplitN(data, ":", 3)[2] fmt.Printf("Message: '%v'\n", msg) } // Thank user when given OP... then seek revenge // TODO: Use regex to parse `data` if strings.Contains(data, "MODE "+IRC_CHANNEL+" +o "+BOT_NICK) { irc <- nick + ": thanks :-)" for _, user := range revenge { rawIrcMsg("KICK " + IRC_CHANNEL + " " + user + " :" + REVENGE_MSG) } revenge = []string{} } // Seek revenge on those who remove bot's OP // TODO: Use regex to parse `data` if strings.Contains(data, "MODE "+IRC_CHANNEL+" -o "+BOT_NICK) { irc <- ":-(" revenge = append(revenge, nick) } // // Re-join if kicked // if fun.ContainsAllStrings(data, "KICK", BOT_NICK) { rawIrcMsg("JOIN " + IRC_CHANNEL) revenge = append(revenge, nick) } // We don't need more than one bot posting Redmine tickets... // TODO: Create p2p bot federation (no master//elected master) if THIS_SERVER_NAME == "openweb-core" { if fun.HasAnyPrefixes(msg, "!ticket ", "!new ", "!bug ") { go func() { ticket := redmine.ParseTicket(msg) if ticket == nil { irc <- "Usage: '![new|bug] project_name New Ticket " + "Subject; New ticket description'" return } resp, err := redmine.CreateTicket(ticket) if err != nil { irc <- "Ticket creation failed: " + err.Error() return } if resp.StatusCode == 404 { irc <- fmt.Sprintf("Project '%v' not found", ticket.Project) } else if resp.StatusCode == 201 { irc <- "Ticket created: " + resp.Header["Location"][0] } else { irc <- "Something bad happened..." log.Printf("resp == %+v\n", resp) body, err := ioutil.ReadAll(resp.Body) if err != nil { irc <- "Error reading response, even!" return } resp.Body.Close() log.Printf("Server response:\n%s\n", body) } return }() } // // Check for messages from other users; send them // for _, oldMsg := range answeringMachine[nick] { irc <- oldMsg } delete(answeringMachine, nick) // // Save user messages to each other // if strings.HasPrefix(msg, "!msg ") { // msg == "!msg elimisteve,ajvb,swiss Check this out: ... " // body == "elimisteve,ajvb,swiss Check this out: ..." // nAndT == {"elimisteve,ajvb,swiss", " Check this out: ..."} // nicks == {"elimisteve", "ajvb", "swiss"} // text == "Check this out: ... (from $SENDER at $TIME)" body := strings.Trim(msg[len("!msg "):], " ") nicksAndText := strings.SplitN(body, " ", 2) if len(nicksAndText) == 2 { // Parse comma-separated list of recipient nicks nicks := strings.Split(nicksAndText[0], ",") // Strip leading spaces, add sender and timestamp text := strings.TrimLeft(nicksAndText[1], " ") text += fmt.Sprintf(" (from %s at %s)", nick, time.Now()) for _, n := range nicks { answeringMachine[n] = append(answeringMachine[n], text) } irc <- nick + ": message(s) saved" } else { irc <- "Missing nicks or message" } } } // // // ADD YOUR CODE (or function calls) HERE // } }