From ba455ac44ff7ef55c3c34d2a863e5ded20f13d7f Mon Sep 17 00:00:00 2001
From: raylu <raylu@cmu.edu>
Date: Sat, 16 Oct 2010 15:38:40 -0400
Subject: [PATCH] !add, !flags, !topic

---
 .gitignore        |  1 +
 Makefile          | 11 +++++++-
 auth.conf.example |  7 +++++
 auth.go           | 70 +++++++++++++++++++++++++++++++++++++++++++++++
 handler.go        | 70 +++++++++++++++++++++++++++++++++++++++++++++--
 rbot.go           |  1 +
 6 files changed, 157 insertions(+), 3 deletions(-)
 create mode 100644 auth.conf.example
 create mode 100644 auth.go

diff --git a/.gitignore b/.gitignore
index 8523e9e..88e7eae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ _test/
 *~
 *.out
 rbot.conf
+auth.conf
diff --git a/Makefile b/Makefile
index 1502278..016998f 100644
--- a/Makefile
+++ b/Makefile
@@ -8,10 +8,11 @@ TARG=rbot
 GOFILES=\
 	rbot.go\
 	handler.go\
+	auth.go
 
 include $(GOROOT)/src/Make.cmd
 
-all: rbot.conf
+all: rbot.conf auth.conf
 
 rbot.conf: rbot.conf.example
 	@if [ -f $@ ] ; then \
@@ -20,3 +21,11 @@ rbot.conf: rbot.conf.example
 		echo cp $< $@ ; \
 		cp $< $@ ; \
 	fi
+
+auth.conf: auth.conf.example
+	@if [ -f $@ ] ; then \
+		echo "auth.conf exists, but auth.conf.example is newer." ; \
+	else \
+		echo cp $< $@ ; \
+		cp $< $@ ; \
+	fi
diff --git a/auth.conf.example b/auth.conf.example
new file mode 100644
index 0000000..2683f17
--- /dev/null
+++ b/auth.conf.example
@@ -0,0 +1,7 @@
+[Rizon]
+owner: f.o.o.r
+
+[Rizon #raylu]
+
+[Rizon #vn-meta]
+chauc.er: at
diff --git a/auth.go b/auth.go
new file mode 100644
index 0000000..84ad719
--- /dev/null
+++ b/auth.go
@@ -0,0 +1,70 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"strings"
+	"irc"
+	"github.com/kless/goconfig/config"
+)
+
+var auth *config.Config
+const authFile = "auth.conf"
+
+func readAuth() {
+	var err os.Error;
+	auth, err = config.ReadDefault(authFile)
+	if (err != nil) {
+		panic(fmt.Sprintf("Auth config error: %s", err))
+	}
+}
+
+func addAccess(conn *irc.Conn, channel, nick, flags string) (string, string) {
+	n := conn.GetNick(nick)
+	if n == nil {
+		return "", ""
+	}
+
+	section := conn.Network + " " + channel
+	cflags, _ := auth.String(section, n.Host)
+
+	nflags := cflags
+	for _, flag := range flags {
+		if strings.IndexRune(cflags, flag) > -1 {
+			// already has the flag
+			continue
+		}
+		nflags += string(flag)
+	}
+
+	auth.AddOption(section, n.Host, nflags)
+	err := auth.WriteFile(authFile, 0644, "")
+	if err != nil {
+		say(conn, channel, "Error while writing to %s", authFile)
+	}
+	// config.WriteFile destroys the config, so
+	readAuth()
+
+	return n.Host, nflags
+}
+
+// passing a flag of "" will check if the user has any access
+func hasAccess(conn *irc.Conn, channel, nick, flag string) bool {
+	n := conn.GetNick(nick)
+	if n == nil {
+		return false
+	}
+
+	if owner, _ := auth.String(conn.Network, "owner"); owner == n.Host {
+		return true
+	}
+
+	flags, err := auth.String(conn.Network + " " + channel, n.Host)
+	if err != nil {
+		return false
+	}
+	if strings.Index(flags, flag) > -1 {
+		return true
+	}
+	return false
+}
diff --git a/handler.go b/handler.go
index 14379a8..b4afc5f 100644
--- a/handler.go
+++ b/handler.go
@@ -15,13 +15,16 @@ import (
 
 var commands = map [string]func(*irc.Conn, string, string, string) {
 	"tr": translate,
+	"flags": flags,
+	"add": add,
+	"topic": topic,
 }
 
 const googleAPIKey = "ABQIAAAA6-N_jl4ETgtMf2M52JJ_WRQjQjNunkAJHIhTdFoxe8Di7fkkYhRRcys7ZxNbH3MIy_MKKcEO4-9_Ag"
 
 func handlePrivmsg(conn *irc.Conn, line *irc.Line) {
 	target := line.Args[0]
-	if target[0] == '#' || target[0] == '&' {
+	if isChannel(target) {
 		// message to a channel
 		var video string
 		if strings.HasPrefix(line.Text, "http://www.youtube.com/watch?v=") {
@@ -50,6 +53,10 @@ func handleMode(conn *irc.Conn, line *irc.Line) {
 	}
 }
 
+func isChannel(target string) bool {
+	return target[0] == '#' || target[0] == '&'
+}
+
 func command(conn *irc.Conn, nick, text, target string) {
 	if !strings.HasPrefix(text, trigger) {
 		return
@@ -69,7 +76,8 @@ func command(conn *irc.Conn, nick, text, target string) {
 }
 
 func say(conn *irc.Conn, target, message string, a ...interface{}) {
-	conn.Privmsg(target, fmt.Sprintf(message, a...))
+	text := strings.Replace(fmt.Sprintf(message, a...), "\n", " ", -1)
+	conn.Privmsg(target, text)
 }
 
 func youtube(conn *irc.Conn, nick, video, channel string) {
@@ -181,3 +189,61 @@ func sayTr(conn *irc.Conn, target string, data interface{}) {
 		say(conn, target, html.UnescapeString(trText))
 	}
 }
+
+func add(conn *irc.Conn, nick, args, channel string) {
+	if !isChannel(channel) {
+		return
+	}
+	if hasAccess(conn, channel, nick, "a") {
+		split := strings.Fields(args)
+		if len(split) != 2 {
+			return
+		}
+		host, nflags := addAccess(conn, channel, split[0], split[1])
+		if host == "" {
+			say(conn, channel, "Could not find nick %s", split[0])
+		} else {
+			say(conn, channel, "%s now has flags %s", host, nflags)
+		}
+	}
+}
+
+func flags(conn *irc.Conn, nick, args, channel string) {
+	if !isChannel(channel) || !hasAccess(conn, channel, nick, "") {
+		return
+	}
+
+	query := args
+	if query == "" {
+		query = nick
+	}
+	n := conn.GetNick(query)
+	if n == nil {
+		say(conn, channel, "Could not find nick %s", query)
+		return
+	}
+
+	if owner, _ := auth.String(conn.Network, "owner"); owner == n.Host {
+		say(conn, channel, "%s is the owner", query)
+		return
+	}
+
+	flags, err := auth.String(conn.Network + " " + channel, n.Host)
+	if err != nil {
+		say(conn, channel, "Error while retrieving flags for %s: %s", n.Host, err)
+		return
+	}
+	say(conn, channel, "%s: %s", n.Host, flags)
+}
+
+func topic(conn *irc.Conn, nick, args, channel string) {
+	if !isChannel(channel) {
+		return
+	}
+	if hasAccess(conn, channel, nick, "t") {
+		if args != "" {
+			conn.Topic(channel, args)
+		}
+		// TODO: appendtopic, query for topic with blank args
+	}
+}
diff --git a/rbot.go b/rbot.go
index 9adfc1f..de5a643 100644
--- a/rbot.go
+++ b/rbot.go
@@ -21,6 +21,7 @@ func main() {
 	}
 
 	trigger = readConfString("DEFAULT", "trigger")
+	readAuth()
 
 	sections = conf.Sections()
 	for _, s := range sections {