~kris/9p

glenda

f9959eb9e0b2761b3a1ca98181e50f5dd7f781ae — Sean Hinchee 7 years ago 5cd758a
add Remove handler, add bullshit, fix config files (need refactoring badly), update help text
6 files changed, 297 insertions(+), 117 deletions(-)

M mux.go
M x/mux/commits.go
M x/mux/config.go
M x/mux/fortunes.go
A x/mux/misc/bullshit
M x/mux/remind.go
M mux.go => mux.go +4 -2
@@ 18,7 18,7 @@ func init() {
	// Register the build-in help command.
	Router.Route("help", "Display this message.", Router.Help)

	Router.Route("fortune", "Display fortunes. Bonus files are (theo troll rsc bullshit terry rob).", Router.Fortunes)
	Router.Route("fortune", "Display fortunes. Bonus files are (theo troll rsc terry rob ken).", Router.Fortunes)
	
	Router.Route("add", "Subscribe to an RSS feed.", Router.Add)
	


@@ 44,7 44,9 @@ func init() {
	
	Router.Route("whiskey", ":tumbler_glass:", Router.Whiskey)
	
	Router.Route("wine", ":wine_glass", Router.Wine)
	Router.Route("wine", ":wine_glass:", Router.Wine)
	
	Router.Route("remindme", "Set a reminder for a given time interval ([int][hmsd] [reminder]).", Router.RemindMe)
	
	Router.Route("bullshit", "Print logical statements with sound grounding.", Router.Bullshit)
}

M x/mux/commits.go => x/mux/commits.go +70 -43
@@ 15,35 15,35 @@ import (
// Listen on the rss feed
func Listener() {
	for {
		for p, _ := range Config.Feeds {
			//fmt.Print("Updating feed: ", Config.Feeds[p].Feed.Title, "\n")
		for p, _ := range Feeds.Feeds {
			//fmt.Print("Updating feed: ", Feeds.Feeds[p].Feed.Title, "\n")
			// maybe only do at init step?
			str := Config.Feeds[p].Feed.UpdateURL
			str := Feeds.Feeds[p].Feed.UpdateURL
			feed, err := rss.Fetch(str)
			if feed != nil {
				Config.Feeds[p].Feed = *feed
				Feeds.Feeds[p].Feed = *feed
			} else {
				//fmt.Println("Got a nil pointer for feed in commits for ", str, " as ", err)
			}
			//err := Config.Feeds[p].Feed.Update()
			//err := Feeds.Feeds[p].Feed.Update()

			if err != nil {
				fmt.Println("Error in updating RSS feed, see: x/mux/commits.go")
				fmt.Printf("%s\n\n", err)
			} else {
				additem := true
				for j, _ := range Config.Feeds[p].Recent {
					if Config.Feeds[p].Recent[j] == Config.Feeds[p].Feed.Items[0].Title {
						//fmt.Println("Checking Recent ", j, " against ", Config.Feeds[p].Feed.Items[0].Title)
				for j, _ := range Feeds.Feeds[p].Recent {
					if Feeds.Feeds[p].Recent[j] == Feeds.Feeds[p].Feed.Items[0].Title {
						//fmt.Println("Checking Recent ", j, " against ", Feeds.Feeds[p].Feed.Items[0].Title)
						additem = false
						break
					}
				}
				if additem {
					// x y z → x y z 0 → y z 0
					fmt.Println("Updating: ", Config.Feeds[p].Feed.Title)
					Config.Feeds[p].Recent = append(Config.Feeds[p].Recent, Config.Feeds[p].Feed.Items[0].Title)
					Config.Feeds[p].Recent = Config.Feeds[p].Recent[1:]
					fmt.Println("Updating: ", Feeds.Feeds[p].Feed.Title)
					Feeds.Feeds[p].Recent = append(Feeds.Feeds[p].Recent, Feeds.Feeds[p].Feed.Items[0].Title)
					Feeds.Feeds[p].Recent = Feeds.Feeds[p].Recent[1:]
					Notify(p)
					break
				}


@@ 59,11 59,11 @@ func Listener() {
// Notify subscribed channels to subscribed feeds
func Notify(id int) {
	resp := ".\n"
	resp += "**" + Config.Feeds[id].Feed.Title + ": **" + "\n"
	resp += Config.Feeds[id].Feed.Items[0].Date.String() + "\n\n"
	resp += "**" + Feeds.Feeds[id].Feed.Title + ": **" + "\n"
	resp += Feeds.Feeds[id].Feed.Items[0].Date.String() + "\n\n"
	// If a 9front feed, extract the user ☺
	if strings.Contains(Config.Feeds[id].Feed.Items[0].Link, "http://code.9front.org/hg/") {
		lines := strings.Split(Config.Feeds[id].Feed.Items[0].Summary, "\n")
	if strings.Contains(Feeds.Feeds[id].Feed.Items[0].Link, "http://code.9front.org/hg/") {
		lines := strings.Split(Feeds.Feeds[id].Feed.Items[0].Summary, "\n")
		for i, v := range lines {
			if strings.Contains(v, "<th style=\"text-align:left;vertical-align:top;\">user</th>") {
				line := html.UnescapeString((lines[i+1])[6:len(lines[i+1])-5])


@@ 72,23 72,23 @@ func Notify(id int) {
			}
		}
	}
	resp += "`" + Config.Feeds[id].Feed.Items[0].Title + "`" + "\n"
	resp += "\n" + Config.Feeds[id].Feed.Items[0].Link + "\n"
	Config.Feeds[id].Feed.Items[0].Read = true
	resp += "`" + Feeds.Feeds[id].Feed.Items[0].Title + "`" + "\n"
	resp += "\n" + Feeds.Feeds[id].Feed.Items[0].Link + "\n"
	Feeds.Feeds[id].Feed.Items[0].Read = true
	resp += "\n"
	
	// Loop through subbed chans and post notification message
	fmt.Println("Looping through subs to notify...")
	for _, v := range Config.Subs {
	for _, v := range Feeds.Subs {
		if v.SubID == id {
			Session.ChannelMessageSend(v.ChanID, resp)
		}
		time.Sleep(10 * time.Millisecond)
	}

	fmt.Println("No new notifys for ", Config.Feeds[id].Feed.UpdateURL)
	fmt.Println(Config.Feeds[id].Feed.Items[0])
	fmt.Println(Config.Feeds[id].Feed.Items[len(Config.Feeds[id].Feed.Items)-1])
	fmt.Println("No new notifys for ", Feeds.Feeds[id].Feed.UpdateURL)
	fmt.Println(Feeds.Feeds[id].Feed.Items[0])
	fmt.Println(Feeds.Feeds[id].Feed.Items[len(Feeds.Feeds[id].Feed.Items)-1])
}




@@ 99,12 99,12 @@ func (m *Mux) Last(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	resp := ".\n"

	id, _ := strconv.Atoi(ctx.Fields[len(ctx.Fields) -1])
	if id >= 0 && id < len(Config.Feeds) {
		resp += "**" + Config.Feeds[id].Feed.Title + ": **" + "\n"
		resp += Config.Feeds[id].Feed.Items[0].Date.String() + "\n\n"
	if id >= 0 && id < len(Feeds.Feeds) {
		resp += "**" + Feeds.Feeds[id].Feed.Title + ": **" + "\n"
		resp += Feeds.Feeds[id].Feed.Items[0].Date.String() + "\n\n"
		// If a 9front feed, extract the user ☺
		if strings.Contains(Config.Feeds[id].Feed.Items[0].Link, "http://code.9front.org/hg/") {
			lines := strings.Split(Config.Feeds[id].Feed.Items[0].Summary, "\n")
		if strings.Contains(Feeds.Feeds[id].Feed.Items[0].Link, "http://code.9front.org/hg/") {
			lines := strings.Split(Feeds.Feeds[id].Feed.Items[0].Summary, "\n")
			for i, v := range lines {
				if strings.Contains(v, "<th style=\"text-align:left;vertical-align:top;\">user</th>") {
					line := html.UnescapeString((lines[i+1])[6:len(lines[i+1])-5])


@@ 113,11 113,11 @@ func (m *Mux) Last(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
				}
			}
		}
		resp += "`" + Config.Feeds[id].Feed.Items[0].Title + "`" + "\n"
		//resp += "\n" + Config.Feeds[id].Feed.Items[0].Summary + "\n"
		resp += "\n" + Config.Feeds[id].Feed.Items[0].Link + "\n"
		Config.Feeds[id].Feed.Items[0].Read = true
		fmt.Println("Last-ing notification: ", Config.Feeds[id].Feed.Items[0])
		resp += "`" + Feeds.Feeds[id].Feed.Items[0].Title + "`" + "\n"
		//resp += "\n" + Feeds.Feeds[id].Feed.Items[0].Summary + "\n"
		resp += "\n" + Feeds.Feeds[id].Feed.Items[0].Link + "\n"
		Feeds.Feeds[id].Feed.Items[0].Read = true
		fmt.Println("Last-ing notification: ", Feeds.Feeds[id].Feed.Items[0])
	} else {
		resp += "Denied fetch. Invalid stream id, see: list command"
	}


@@ 133,7 133,7 @@ func (m *Mux) Last(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
func (m *Mux) List(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	resp := "```\n"

	for p, v := range Config.Feeds {
	for p, v := range Feeds.Feeds {
		resp += strconv.Itoa(p) + ": " + v.Feed.Title + ", " + v.Feed.Link + "\n"
	}



@@ 144,15 144,15 @@ func (m *Mux) List(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	return
}

// Subscribe to a feed
// Add a given feed to be tracked within glenda
func (m *Mux) Add(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	//http://code.9front.org/hg/plan9front/rss-log
	resp := "```\n"
	// URL to feed should be last item
	url := ctx.Fields[len(ctx.Fields) -1]
	fmt.Println("Proposed subscribe for: ", url)
	fmt.Println("Proposed addition for: ", url)
	
	for _, v := range Config.Feeds {
	for _, v := range Feeds.Feeds {
		// this is bad matching, can't have two bitbucket url's?
		if strings.Contains(url, v.Feed.UpdateURL) {
			//fmt.Println(url)


@@ 180,7 180,7 @@ func (m *Mux) Add(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	tmpFeed.Feed = *feed
	// Maybe make the size here a Config variable
	tmpFeed.Recent = make([]string, 3)
	Config.Feeds = append(Config.Feeds, tmpFeed)
	Feeds.Feeds = append(Feeds.Feeds, tmpFeed)
	resp += "Added."
	
	resp += "```\n"


@@ 189,16 189,43 @@ func (m *Mux) Add(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	return
}

// Stop tracking a given feed
func (m *Mux) Remove(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	//http://code.9front.org/hg/plan9front/rss-log
	resp := "```\n"
	// ID to feed should be last item
	ID, err := strconv.Atoi(ctx.Fields[len(ctx.Fields) -1])
	fmt.Println("Proposed removal for: ", ID)
	
	if ID < 0 || ID > len(Feeds.Feeds) || err != nil {
		resp += "Denied! Feed does not exist!"
		resp += "```\n"
		goto REND;
	}

	

	// Might not be thread safe
	Feeds.Feeds = append(Feeds.Feeds[:ID], Feeds.Feeds[ID+1:]...)
	resp += "Removed."
	
	REND:
	resp += "```\n"
	ds.ChannelMessageSend(dm.ChannelID, resp)

	return
}

// Subscribe current channel to notifications from a given feed id
func (m *Mux) Subscribe(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	resp := "\n"

	id, _ := strconv.Atoi(ctx.Fields[len(ctx.Fields) -1])
	if id >= 0 && id < len(Config.Feeds) {
	if id >= 0 && id < len(Feeds.Feeds) {
		var sub Subscription

		// Check if already subscribed
		for _, v := range Config.Subs {
		for _, v := range Feeds.Subs {
			if v.ChanID == dm.ChannelID && v.SubID == id {
				resp += "Denied subscription. Already subscribed in this channel."
				goto NOSUB


@@ 209,7 236,7 @@ func (m *Mux) Subscribe(ds *discordgo.Session, dm *discordgo.Message, ctx *Conte
		sub.SubID = id
	
		// Might not be thread-safe
		Config.Subs = append(Config.Subs, sub)
		Feeds.Subs = append(Feeds.Subs, sub)
		resp += "Subscribed."
		NOSUB:
	} else {


@@ 228,13 255,13 @@ func (m *Mux) Unsubscribe(ds *discordgo.Session, dm *discordgo.Message, ctx *Con
	resp := "\n"

	id, _ := strconv.Atoi(ctx.Fields[len(ctx.Fields) -1])
	if id >= 0 && id < len(Config.Feeds) {
	if id >= 0 && id < len(Feeds.Feeds) {
		// Check if subscribed
		removed := false
		for i, v := range Config.Subs {
		for i, v := range Feeds.Subs {
			if v.ChanID == dm.ChannelID && v.SubID == id {
				removed = true
				Config.Subs = Config.Subs[:i+copy(Config.Subs[i:], Config.Subs[i+1:])]
				Feeds.Subs = Feeds.Subs[:i+copy(Feeds.Subs[i:], Feeds.Subs[i+1:])]
			}
		}
		

M x/mux/config.go => x/mux/config.go +138 -5
@@ 9,11 9,14 @@ import (
	"strings"
	"github.com/SlyMarbo/rss"
	"github.com/bwmarrin/discordgo"
	"container/list"
)


// Global variables are bad
var Config Configuration
var Feeds	Feeder
var Rems *list.List
var Session *discordgo.Session

// Stores subscription information for channels related to RSS feeds


@@ 28,21 31,30 @@ type Feed struct {
	Recent	[]string
}

// Stores config for current state
type Configuration struct {
type Feeder struct {
	Feeds	[]Feed
	Subs		[]Subscription
}

// Stores config for current state
type Configuration struct {

}

// Initializes current config (called once at start) ­ just .Read()?
func (c *Configuration) Init(s *discordgo.Session) {
	Rems = list.New()
	c.Read()
	Session = s
	for id, _ := range Config.Feeds {
	for id, _ := range Feeds.Feeds {
		// maybe only do at init step?
		str := Config.Feeds[id].Feed.UpdateURL
		str := Feeds.Feeds[id].Feed.UpdateURL
		feed, _ := rss.Fetch(str)
		Config.Feeds[id].Feed = *feed
		if feed != nil {
			Feeds.Feeds[id].Feed = *feed
		} else {
			fmt.Println("Failed to fetch feed: ", id)
		}
	}
	go Listener()
}


@@ 76,6 88,64 @@ func (c *Configuration) Write() (rerr error) {
	return
}

// Writes the Feed config file
func (c *Feeder) Write() (rerr error) {
	FWRT:
	rerr = nil
	f, err := os.OpenFile("./cfg/feeds.cfg", os.O_RDWR, 0666)
	defer f.Close()
	if err != nil {
		if strings.Contains(err.Error(), "no such file or directory") {
			// danger: this can go infinite
			Config.Setup()
			goto FWRT
		} else {
			fmt.Println("Error opening feeds config (w), see: config.go")
			fmt.Printf("%s\n", err)
			rerr = err
		}
	} else {
		e := json.NewEncoder(f)
		err = e.Encode(Feeds)
		if err != nil {
			fmt.Println("Error writing feeds config, see: config.go")
			fmt.Printf("%s\n", err)
			rerr = err
		}
	}

	return
}

// Writes the Reminders config file
func RemindersWrite() (rerr error) {
	RWRT:
	rerr = nil
	f, err := os.OpenFile("./cfg/reminders.cfg", os.O_RDWR, 0666)
	defer f.Close()
	if err != nil {
		if strings.Contains(err.Error(), "no such file or directory") {
			// danger: this can go infinite
			Config.Setup()
			goto RWRT
		} else {
			fmt.Println("Error opening reminders config (w), see: config.go")
			fmt.Printf("%s\n", err)
			rerr = err
		}
	} else {
		e := json.NewEncoder(f)
		err = e.Encode(*Rems)
		if err != nil {
			fmt.Println("Error writing reminders config, see: config.go")
			fmt.Printf("%s\n", err)
			rerr = err
		}
	}

	return
}

// Reads current config into memory
func (c *Configuration) Read() (rerr error) {
	RD:


@@ 101,6 171,55 @@ func (c *Configuration) Read() (rerr error) {
			Config.Write()
		}
	}
	f.Close()
	
	// Feeds
	f, err = os.Open("./cfg/feeds.cfg")
	if err != nil {
		if strings.Contains(err.Error(), "no such file or directory") {
			// danger: this can go infinite
			Config.Setup()
			goto RD
		} else {
			fmt.Println("Error opening feeds config (r), see: config.go")
			fmt.Printf("%s\n", err)
			rerr = err
		}
	} else {
		d := json.NewDecoder(f)
		err = d.Decode(&Feeds)
		if err != nil {
			fmt.Println("Error reading feeds config, see: config.go")
			fmt.Printf("%s\n", err)
			rerr = err
			Feeds.Write()
		}
	}
	f.Close()
	
	// Reminders
	f, err = os.Open("./cfg/reminders.cfg")
	if err != nil {
		if strings.Contains(err.Error(), "no such file or directory") {
			// danger: this can go infinite
			Config.Setup()
			goto RD
		} else {
			fmt.Println("Error opening reminders config (r), see: config.go")
			fmt.Printf("%s\n", err)
			rerr = err
		}
	} else {
		d := json.NewDecoder(f)
		err = d.Decode(Rems)
		if err != nil {
			fmt.Println("Error reading reminders config, see: config.go")
			fmt.Printf("%s\n", err)
			rerr = err
			RemindersWrite()
		}
	}
	f.Close()

	return
}


@@ 118,4 237,18 @@ func (c *Configuration) Setup() {
		fmt.Println("Error in making cfg file, see: config.go")
		fmt.Println(err)
	}
	
	// Feeds
	_, err = os.Create("cfg/feeds.cfg")
	if err != nil {
		fmt.Println("Error in making feed cfg file, see: config.go")
		fmt.Println(err)
	}
	
	// Reminders
	_, err = os.Create("cfg/reminders.cfg")
	if err != nil {
		fmt.Println("Error in making reminders cfg file, see: config.go")
		fmt.Println(err)
	}
}

M x/mux/fortunes.go => x/mux/fortunes.go +17 -2
@@ 21,8 21,6 @@ func (m *Mux) Fortunes(ds *discordgo.Session, dm *discordgo.Message, ctx *Contex
		f = "troll"
	} else if strings.Contains(dm.Content, "rsc") {
		f = "rsc"
	} else if strings.Contains(dm.Content, "bullshit") {
		f = "bullshit"
	} else if strings.Contains(dm.Content, "terry") {
		f = "terry"
	} else if strings.Contains(dm.Content, "rob") {


@@ 48,3 46,20 @@ func (m *Mux) Fortunes(ds *discordgo.Session, dm *discordgo.Message, ctx *Contex
	return
}

// Display bullshit from 9front
func (m *Mux) Bullshit(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	resp := "```\n"
	
	out, err := exec.Command("./x/mux/misc/bullshit").Output()
	if err != nil {
		resp += "No bullshit script found."
		goto END
	} else {
		resp += string(out)
	}
	
	END:
	resp += "```\n"
	ds.ChannelMessageSend(dm.ChannelID, resp)
	return
}

A x/mux/misc/bullshit => x/mux/misc/bullshit +59 -0
@@ 0,0 1,59 @@
#!/usr/bin/env rc
# This is 9front bullshit made unix portable
MIRROR=/usr/share/mirror/plan9front
rfork e
if(~ $#1 0)
	file=$MIRROR/lib/bullshit
if not
	file=$1
awk '
function min(a, b){
	if(a > b)
		return b
	else
		return a
}
function suffix(){
	if(hassuffix = (rand() < 0.2))
		return suffixes[int(rand() * nsuffix)]
	return ""
}
$2 == "*" { protocol[nprotocol++] = $1; next }
$2 == "$" { end[nend++] = $1; next }
$2 == "%" { suffixes[nsuffix++] = $1; next }
$2 == "^" { start[nstart++] = $1; next }
$2 == "|" { noend[nword] = 1 }
{ word[nword++] = $1; next }
END {
	srand()
	last = -1
	out = 0
	total = int(rand() * 7) + 3
	n = int(rand() * min(total - out, 3))
	for(i = 0; i < n; i++)
		printf "%s ", start[int(rand() * nstart)]
	out += n
	hassuffix = 0
	n = int(rand() * min(total - out, 3))
	for(i = 0; i < n; i++)
		printf "%s%s ", word[last = int(rand() * nword)], suffix()
	out += n
	if(rand() > 0.5){
		n = int(rand() * 3)
		for(i = 0; i < n; i++)
			printf "%s %s", protocol[int(rand() * nprotocol)], (i != n-1) ? "over " : ""
		out += n
		last = -1
		hassuffix = 0
	}
	n = int(rand() * min(total - out, 3))
	if(out + n == 1 || last == -1)
		n += 2
	for(i = 0; i < n; i++)
		printf "%s%s ", word[last = int(rand() * nword)], suffix()
	if(rand() < 0.1 || (last >= 0 && noend[last]) || hassuffix)
		printf "%s ", end[int(rand() * nend)]
	print ""
}
' $file


M x/mux/remind.go => x/mux/remind.go +9 -65
@@ 4,11 4,8 @@ import (
	"github.com/bwmarrin/discordgo"
	"time"
sc	"strconv"
	"container/list"
	"fmt"
	"os"
	"encoding/json"
	"strings"
//	"container/list"
//	"fmt"
)




@@ 26,74 23,21 @@ type Reminder struct {

// Reminder daemon process that gets started in main and listens on RemChan
func Reminders() {
	// TODO -- Should be a heap and more robus (fix config first)
	
	rems := list.New()
	
	RD:
	f, err := os.Open("./cfg/reminders.cfg")
	
	write := func() (rerr error) {
		e := json.NewEncoder(f)
		err = e.Encode(rems)
		if err != nil {
			fmt.Println("Error writing config, see: remind.go")
			fmt.Printf("%s\n", err)
			rerr = err
		}
		return
	}
	
	setup := func() (rerr error) {
		
		err := os.Mkdir("./cfg", 0774)
		if err != nil {
			fmt.Println("Error in making cfg dir, see: remind.go")
			fmt.Println(err)
		}
		
		_, err = os.Create("cfg/reminders.cfg")
		if err != nil {
			fmt.Println("Error in making cfg file, see: remind.go")
			fmt.Println(err)
		}
		rerr = err
		return
	}
	
	defer f.Close()
	if err != nil {
		if strings.Contains(err.Error(), "no such file or directory") {
			// danger: this can go infinite
			setup()
			goto RD
		} else {
			fmt.Println("Error opening config (r), see: remind.go")
			fmt.Printf("%s\n", err)
		}
	} else {
		d := json.NewDecoder(f)
		err = d.Decode(&rems)
		if err != nil {
			fmt.Println("Error reading config, see: remind.go")
			fmt.Printf("%s\n", err)
			
			write()
		}
	}
	// TODO -- Should be a heap and more robust (fix config first)
	
	//Rems := list.New()

	// Handle reminders
	for {
		select {
		case r := <- RemChan:
			// Handle new reminder
			rems.PushBack(r)
			write()
			Rems.PushBack(r)
			//write()
			
		default:
			// Check for any due reminders
			for e := rems.Front(); ; e = e.Next() {
			for e := Rems.Front(); ; e = e.Next() {
				if e == nil {
					break
				}


@@ 102,8 46,8 @@ func Reminders() {
				if time.Now().After(r.NotifyAfter) {
					// If we have passed the time of desired notification
					r.Session.ChannelMessageSend(r.ChannelID, r.User.Mention() + " -- " + r.Reason)
					rems.Remove(e)
					write()
					Rems.Remove(e)
					//write()
				}
			}