Example #1
0
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
		//
	}
}