package main import ( "irc" "fmt" "strings" "http" "io/ioutil" "json" "container/vector" "html" "xml" "strconv" ) var commands = map [string]func(*irc.Conn, *irc.Nick, string, string) { "tr": translate, "flags": flags, "add": add, "remove": remove, "topic": topic, "appendtopic": appendtopic, "say": csay, "halfop": halfop, "hop": halfop, "op": op, "deop": deop, "dehalfop": dehalfop, "dehop": dehalfop, "kick": kick, "k": kick, } const googleAPIKey = "ABQIAAAA6-N_jl4ETgtMf2M52JJ_WRQjQjNunkAJHIhTdFoxe8Di7fkkYhRRcys7ZxNbH3MIy_MKKcEO4-9_Ag" func handlePrivmsg(conn *irc.Conn, line *irc.Line) { nick := conn.GetNick(line.Nick) if nick == nil { return } nick.Host = line.Host target := line.Args[0] if isChannel(target) { // message to a channel var video string if strings.HasPrefix(line.Text, "http://www.youtube.com/watch?v=") { video = line.Text[31:] } else if strings.HasPrefix(line.Text, "http://www.youtube.com/watch?v=") { video = line.Text[27:] } if video != "" { if amp := strings.Index(video, "&"); amp > -1 { video = video[0:amp] } if pound := strings.Index(video, "#"); pound > -1 { video = video[0:pound] } youtube(conn, nick, video, target) } else { command(conn, nick, line.Text, target) } } else if target == conn.Me.Nick { // message to us command(conn, nick, line.Text, line.Nick) } } func handleMode(conn *irc.Conn, line *irc.Line) { if line.Args[0] == conn.Me.Nick && line.Text == "+r" { autojoin(conn) } } func handleInvite(conn *irc.Conn, line *irc.Line) { if line.Args[0] != conn.Me.Nick { return } user := line.Src[strings.Index(line.Src, "!")+1:] if user[0] == '~' { user = user[1:] } owner, _ := auth.String(conn.Network, "owner") if user == owner { conn.Join(line.Text) } } func isChannel(target string) bool { return target[0] == '#' || target[0] == '&' } func command(conn *irc.Conn, nick *irc.Nick, text, target string) { if !strings.HasPrefix(text, trigger) { return } split := strings.Split(text, " ", 2) if len(split[0]) < 2 { return } handler := commands[split[0][1:]] if handler != nil { if len(split) > 1 { handler(conn, nick, split[1], target) } else { handler(conn, nick, "", target) } } } func say(conn *irc.Conn, target, message string, a ...interface{}) { text := strings.Replace(fmt.Sprintf(message, a...), "\n", " ", -1) if isChannel(target) { conn.Privmsg(target, text) } else { conn.Notice(target, text) } } func youtube(conn *irc.Conn, nick *irc.Nick, video, channel string) { url := fmt.Sprintf("http://gdata.youtube.com/feeds/api/videos/%s?v=2", video) response, _, err := http.Get(url) defer response.Body.Close() if err != nil { return } type duration struct { Seconds string "attr" } type group struct { Duration duration } type entry struct { Title string Group group } var yte = entry{"", group{duration{""}}} err = xml.Unmarshal(response.Body, &yte) if err != nil { return } seconds, err := strconv.Atoui(yte.Group.Duration.Seconds) if err == nil { minutes := seconds / 60 seconds = seconds % 60 hours := minutes / 60 minutes = minutes % 60 var durationStr string if hours > 0 { durationStr = fmt.Sprintf("%d:%02d:%02d", hours, minutes, seconds) } else { durationStr = fmt.Sprintf("%02d:%02d", minutes, seconds) } say(conn, channel, "%s's video: %s, %s", nick.Nick, yte.Title, durationStr) } else { say(conn, channel, "%s's video: %s", nick.Nick, yte.Title) } } // this allows target to be a channel or a privmsg in which the channel is the first argument // passing a flag of "" will check if the user has any access // returns the channel the user has access on and remaining args // or a blank channel if the user doesn't have access on that channel func parseAccess(conn *irc.Conn, nick *irc.Nick, target, args, flag string) (string, string) { channel, args := parseChannel(target, args) if channel == "" { return "", args } if hasAccess(conn, nick, channel, flag) { return channel, args } return "", args } func parseChannel(target, args string) (string, string) { var channel string if isChannel(target) { channel = target } else { split := strings.Split(args, " ", 2) if split[0] != "" && isChannel(split[0]) { channel = split[0] if len(split) == 2 { args = split[1] } else { args = "" } } else { return "", args } } return channel, args } func translate(conn *irc.Conn, nick *irc.Nick, args, target string) { var langPairs vector.StringVector for { split := strings.Split(args, " ", 2) if len(split) == 2 && len(split[0]) == 5 && split[0][2] == '|' { langPairs.Push("&langpair=" + split[0]) args = split[1] } else { break } } var url string if langPairs.Len() > 0 { // translate langPairsSlice := []string(langPairs) url = fmt.Sprintf("http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=%s%s&key=%s", http.URLEscape(args), strings.Join(langPairsSlice, ""), googleAPIKey) } else { // language detect url = fmt.Sprintf("http://ajax.googleapis.com/ajax/services/language/detect?v=1.0&q=%s&key=%s", http.URLEscape(args), googleAPIKey) } response, _, err := http.Get(url) defer response.Body.Close() if err != nil { say(conn, target, "%s: Error while requesting translation", nick.Nick); return } b, err := ioutil.ReadAll(response.Body) if err != nil { say(conn, target, "%s: Error while downloading translation", nick.Nick); return } var result map[string]interface{} err = json.Unmarshal(b, &result) if err != nil { say(conn, target, "%s: Error while decoding translation", nick.Nick); return } if result["responseStatus"] != float64(200) { say(conn, target, "%s: %s", nick.Nick, result["responseDetails"]); return } if langPairs.Len() > 0 { // translate sayTr(conn, target, result["responseData"]) } else { // language detect var data map[string]interface{} = result["responseData"].(map[string]interface{}) say(conn, target, "Language: %s, confidence: %f, is reliable: %t", data["language"], data["confidence"], data["isReliable"]) } } func sayTr(conn *irc.Conn, target string, data interface{}) { switch t := data.(type) { case []interface{}: var dataList []interface{} = data.([]interface{}) for _, d := range dataList { var innerData map[string]interface{} = d.(map[string]interface{}) sayTr(conn, target, innerData["responseData"]) } case map[string]interface{}: trText := data.(map[string]interface{})["translatedText"].(string) say(conn, target, html.UnescapeString(trText)) } } func add(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "a") if channel == "" { return } split := strings.Fields(args) if len(split) != 2 { return } host, nflags := addAccess(conn, channel, split[0], strings.TrimSpace(split[1])) if host == "" { say(conn, target, "Could not find nick %s", split[0]) } else { say(conn, target, "%s now has flags %s", host, nflags) } } func remove(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "a") if channel == "" { return } split := strings.Fields(args) if len(split) == 2 { host, nflags := removeAccess(conn, channel, split[0], strings.TrimSpace(split[1])) if host == "" { say(conn, target, "Could not find nick %s", split[0]) } else { say(conn, target, "%s now has flags %s", host, nflags) } } else if len(split) == 1 { host, removed := removeUser(conn, channel, split[0]) if host == "" { say(conn, target, "Could not find nick %s", split[0]) } else if removed { say(conn, target, "Removed %s", host) } else { say(conn, target, "%s did not have any flags", host) } } } func flags(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "") if channel == "" { return } query := strings.TrimSpace(args) if query == "" { query = nick.Nick } n := conn.GetNick(query) if n == nil { say(conn, target, "Could not find nick %s", query) return } user := user(n) if owner, _ := auth.String(conn.Network, "owner"); owner == user { say(conn, target, "%s is the owner", user) return } flags, _ := auth.String(conn.Network + " " + channel, user) if flags == "" { say(conn, target, "%s has no flags", user) } else { say(conn, target, "%s: %s", user, flags) } } func topic(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "t") if channel == "" { return } section := conn.Network + " " + channel if args != "" { updateConf(section, "basetopic", args) conn.Topic(channel, args) } else { basetopic, _ := conf.String(section, "basetopic") say(conn, nick.Nick, "Basetopic: %s", basetopic) } } func appendtopic(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "t") if channel == "" { return } c := conn.GetChannel(channel) if c == nil { say(conn, target, "Error while getting channel information for %s", channel) return } section := conn.Network + " " + channel basetopic, _ := conf.String(section, "basetopic") if basetopic == "" || !strings.HasPrefix(c.Topic, basetopic) { basetopic = c.Topic say(conn, nick.Nick, "New basetopic: %s", basetopic) updateConf(section, "basetopic", basetopic) } conn.Topic(channel, basetopic + args) } func csay(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "s") if channel != "" { say(conn, channel, args) } } func op(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "o") if channel == "" { return } if args == "" { conn.Mode(channel, "+o " + nick.Nick) } else { ops := strings.TrimSpace(args) count := strings.Count(ops, " ") + 1 modestring := "+" + strings.Repeat("o", count) + " " + ops conn.Mode(channel, modestring) } } func deop(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "o") if channel == "" { return } if args == "" { conn.Mode(channel, "-o " + nick.Nick) } else { ops := strings.TrimSpace(args) count := strings.Count(ops, " ") + 1 modestring := "-" + strings.Repeat("o", count) + " " + ops conn.Mode(channel, modestring) } } func halfop(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "oh") if channel == "" { return } if args == "" { conn.Mode(channel, "+h " + nick.Nick) } else { // giving others +h requires o if !hasAccess(conn, nick, channel, "o") { return } halfops := strings.TrimSpace(args) count := strings.Count(halfops, " ") + 1 modestring := "+" + strings.Repeat("h", count) + " " + halfops conn.Mode(channel, modestring) } } func dehalfop(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "oh") if channel == "" { return } if args == "" { conn.Mode(channel, "-h " + nick.Nick) } else { if !hasAccess(conn, nick, channel, "o") { return } halfops := strings.TrimSpace(args) count := strings.Count(halfops, " ") + 1 modestring := "-" + strings.Repeat("h", count) + " " + halfops conn.Mode(channel, modestring) } } func kick(conn *irc.Conn, nick *irc.Nick, args, target string) { channel, args := parseAccess(conn, nick, target, args, "oh") if channel == "" || args == "" { return } split := strings.Split(args, " ", 2) reason := "(" + nick.Nick + ")" if len(split) == 2 { reason += " " + split[1] } conn.Kick(channel, split[0], reason) }