~kris/9p

glenda

aa73b6813c196df20b30a5f8094beb4ede55d6d0 — Henesy 5 years ago a5acd73
migrate config to One Big File ;; make a map of a=b values for config ;; rewrite reminders to be a slice
5 files changed, 101 insertions(+), 210 deletions(-)

M main.go
M x/mux/commits.go
M x/mux/config.go
M x/mux/misc.go
M x/mux/remind.go
M main.go => main.go +1 -1
@@ 13,7 13,7 @@ import (
sc	"strconv"
)

const Version = "v0.1.0-alpha"
const Version = "v0.1.1"

// Session is declared in the global space so it can be easily used
// throughout this program.

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


@@ 59,11 59,11 @@ func Listener() {
// Notify subscribed channels to subscribed feeds
func Notify(id int) {
	resp := ".\n"
	resp += "**" + Feeds.Feeds[id].Feed.Title + ": **" + "\n"
	resp += Feeds.Feeds[id].Feed.Items[0].Date.String() + "\n\n"
	resp += "**" + Config.Feeds[id].Feed.Title + ": **" + "\n"
	resp += Config.Feeds[id].Feed.Items[0].Date.String() + "\n\n"
	// If a 9front feed, extract the user ☺
	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")
	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")
		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 += "`" + 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 += "`" + 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 += "\n"
	
	// Loop through subbed chans and post notification message
	fmt.Println("Looping through subs to notify...")
	for _, v := range Feeds.Subs {
	for _, v := range Config.Subs {
		if v.SubID == id {
			Session.ChannelMessageSend(v.ChanID, resp)
		}
		time.Sleep(10 * time.Millisecond)
	}

	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])
	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])
}




@@ 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(Feeds.Feeds) {
		resp += "**" + Feeds.Feeds[id].Feed.Title + ": **" + "\n"
		resp += Feeds.Feeds[id].Feed.Items[0].Date.String() + "\n\n"
	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 a 9front feed, extract the user ☺
		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")
		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")
			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 += "`" + 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])
		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])
	} 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 Feeds.Feeds {
	for p, v := range Config.Feeds {
		resp += strconv.Itoa(p) + ": " + v.Feed.Title + ", " + v.Feed.Link + "\n"
	}



@@ 152,7 152,7 @@ func (m *Mux) Add(ds *discordgo.Session, dm *discordgo.Message, ctx *Context) {
	url := ctx.Fields[len(ctx.Fields) -1]
	fmt.Println("Proposed addition for: ", url)
	
	for _, v := range Feeds.Feeds {
	for _, v := range Config.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)
	Feeds.Feeds = append(Feeds.Feeds, tmpFeed)
	Config.Feeds = append(Config.Feeds, tmpFeed)
	resp += "Added."
	
	resp += "```\n"


@@ 197,7 197,7 @@ func (m *Mux) Remove(ds *discordgo.Session, dm *discordgo.Message, ctx *Context)
	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 {
	if ID < 0 || ID > len(Config.Feeds) || err != nil {
		resp += "Denied! Feed does not exist!"
		resp += "```\n"
		goto REND;


@@ 206,7 206,7 @@ func (m *Mux) Remove(ds *discordgo.Session, dm *discordgo.Message, ctx *Context)
	

	// Might not be thread safe
	Feeds.Feeds = append(Feeds.Feeds[:ID], Feeds.Feeds[ID+1:]...)
	Config.Feeds = append(Config.Feeds[:ID], Config.Feeds[ID+1:]...)
	resp += "Removed."
	
	REND:


@@ 221,11 221,11 @@ func (m *Mux) Subscribe(ds *discordgo.Session, dm *discordgo.Message, ctx *Conte
	resp := "\n"

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

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


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


@@ 255,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(Feeds.Feeds) {
	if id >= 0 && id < len(Config.Feeds) {
		// Check if subscribed
		removed := false
		for i, v := range Feeds.Subs {
		for i, v := range Config.Subs {
			if v.ChanID == dm.ChannelID && v.SubID == id {
				removed = true
				Feeds.Subs = Feeds.Subs[:i+copy(Feeds.Subs[i:], Feeds.Subs[i+1:])]
				Config.Subs = Config.Subs[:i+copy(Config.Subs[i:], Config.Subs[i+1:])]
			}
		}
		

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


// 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


@@ 38,40 36,49 @@ type Feeder struct {

// Stores config for current state
type Configuration struct {

	Db			map[string]string
	Feeder
	Reminders []Reminder
}

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

// Writes current config
func (c *Configuration) Write() (rerr error) {
	WRT:
	WRITE:
	rerr = nil
	f, err := os.OpenFile("./cfg/glenda.cfg", os.O_RDWR, 0666)
	f, err := os.OpenFile(c.Db["dir"] + "/" + c.Db["name"], os.O_RDWR, 0666)
	defer f.Close()
	if err != nil {
		if strings.Contains(err.Error(), "no such file or directory") {
			// Create and try again
			// danger: this can go infinite
			Config.Setup()
			goto WRT
			time.Sleep(5 * time.Millisecond)
			goto WRITE
		} else {
			fmt.Println("Error opening config (w), see: config.go")
			fmt.Printf("%s\n", err)


@@ 90,74 97,16 @@ 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:
	f, err := os.Open("./cfg/glenda.cfg")
	READ:
	f, err := os.Open(c.Db["dir"] + "/" + c.Db["name"])
	defer f.Close()
	if err != nil {
		if strings.Contains(err.Error(), "no such file or directory") {
			// danger: this can go infinite
			Config.Setup()
			goto RD
			goto READ
		} else {
			fmt.Println("Error opening config (r), see: config.go")
			fmt.Printf("%s\n", err)


@@ 173,85 122,21 @@ 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
			Rems = list.New()
			RemindersWrite()
		}
	}
	f.Close()

	return
}

// Set up config for the first time (if one doesn't exist)
func (c *Configuration) Setup() {
	err := os.Mkdir("./cfg", 0774)
	err := os.Mkdir(c.Db["dir"], 0774)
	if err != nil {
		fmt.Println("Error in making cfg dir, see: config.go")
		fmt.Println(err)
	}
	
	_, err = os.Create("cfg/glenda.cfg")
	_, err = os.Create(c.Db["dir"] + "/" + c.Db["name"])
	if err != nil {
		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/misc.go => x/mux/misc.go +0 -13
@@ 44,19 44,6 @@ func dump() string {
	} else {
		resp += "Ok."
	}
	err = Feeds.Write()
	if err != nil {
		resp += "Dump feeds failed. Check logs.\n"
	} else {
		resp += "Ok."
	}
	err = RemindersWrite()
	if err != nil {
		resp += "Dump reminders failed. Check logs.\n"
	} else {
		resp += "Ok."
	}

	resp += "\n"
	return resp
}

M x/mux/remind.go => x/mux/remind.go +33 -14
@@ 25,33 25,52 @@ type Reminder struct {
func Reminders() {
	// TODO -- Should be a heap and more robust (fix config first)
	
	//Rems := list.New()
	//Config.Reminders := list.New()

	// Handle reminders
	for {
		select {
		case r := <- RemChan:
			// Handle new reminder
			Rems.PushBack(r)
			Config.Reminders = append(Config.Reminders, r)
			//write()
			
		default:
			// Check for any due reminders
			if Rems != nil {
				if Rems.Front() != nil {
					for e := Rems.Front(); ; e = e.Next() {
						if e == nil {
							break
			for i, r := range Config.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)
					
					// Delete the reminder
					// God this is ugly
					switch len(Config.Reminders) {
					case 1:
						// Make empty safely
						Config.Reminders = []Reminder{}
					case 2:
						// Binary switch
						switch i {
						case 0:
							Config.Reminders = []Reminder{Config.Reminders[1]}
						case 1:
							Config.Reminders = []Reminder{Config.Reminders[0]}
						}
						
						r, _ := e.Value.(Reminder)
						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()
					default:
						switch i {
						case len(Config.Reminders) - 1:
							// Last
							Config.Reminders = Config.Reminders[:i]
						case 0:
							// First
							Config.Reminders = Config.Reminders[i+1:]
						default:
							// Not first nor last
							Config.Reminders = append(Config.Reminders[:i], Config.Reminders[i+1:]...)
						}
					}

					//write()
				}
			}