| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -20,9 +20,10 @@ import (
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				/* Plugin State */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				type AoCState struct {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					model         helperbot.Model
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					boardId       string
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					sessionCookie string
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					model              helperbot.Model
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					boardId            string
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					sessionCookie      string
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					sessionNeedsUpdate bool
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					aoc *aoc.AoC
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -47,6 +48,8 @@ func (s *AoCState) Initialize(m helperbot.Model) error {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					aocSession = strings.TrimSpace(aocSession)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil || aocSession == "" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.RequestSessionCookie()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.sessionCookie = aocSession
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					aocChannelId, err = s.getChannelId()
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -64,44 +67,27 @@ func (s *AoCState) Initialize(m helperbot.Model) error {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) ProcessMessage(m helperbot.Message) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if m.GetSource() == "slack" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						slackMsg := m.GetMessage()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						msgPts := strings.Fields(slackMsg.Text)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if len(msgPts) < 1 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if len(slackMsg.Channel) == 0 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if msgPts[0] != "!aoc" || len(msgPts) < 2 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						msgPts = msgPts[1:]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						yr, err := strconv.Atoi(msgPts[0])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							yr = s.GetLatestYear()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							msgPts = msgPts[1:]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						switch msgPts[0] {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case "ping":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Channel: slackMsg.Channel,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Text:    ":christmas_tree: PONG :christmas_tree:",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case "top":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							var txt string
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if yr == -1 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								var err error
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								txt, err = s.DoTopForAll()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									txt = "Error calculating all-time tops"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								txt = s.DoTopForYear(yr)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						switch slackMsg.Channel[0] {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case 'C':
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							s.ProcessChannelMessage(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						case 'D':
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							admin, err := s.model.GetSlackAdminDMId()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.model.SendMessage(s.Name(), "error", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Type: "error",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Text: "Error getting Admin DM Id",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Time: time.Now(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if slackMsg.Channel == admin {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.ProcessAdminDirectMessage(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.ProcessDirectMessage(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Channel: slackMsg.Channel,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Text:    txt,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -130,47 +116,18 @@ func (s *AoCState) NewAoC() error {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) runLoop() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					for {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						channelId, err := s.getChannelId()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						_, err := s.getChannelId()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							// This plugin fails without a channel id
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						for _, yr := range s.GetListOfAoCYears() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							l, err := s.aoc.GetLeaderboard(yr)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								msg := slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Type: "error",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Text: fmt.Sprintf("Error processing leaderboard (%d)", yr),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Time: time.Now(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.model.SendMessage(s.Name(), "main", msg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								msg := slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Type: "success",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Text: fmt.Sprintf("Received leaderboard (%d)", yr),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Time: time.Now(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.model.SendMessage(s.Name(), "main", msg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								// Compare the new leaderboard to the saved one
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								for _, v := range l.Members {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									mbr, err := s.getMember(l.Event, v.ID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
										continue
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									if mbr.Stars != v.Stars {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
										s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
											Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
											Channel: channelId,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
											Text:    ":christmas_tree: " + v.Name + " now has " + strconv.Itoa(v.Stars) + " stars! :christmas_tree:",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
										})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								// Save the leaderboard to the db
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.saveLeaderboard(l)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if !s.sessionNeedsUpdate {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.AoCBoardCheckAndUpdate(yr)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								time.Sleep(time.Minute)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							time.Sleep(time.Minute)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						time.Sleep(time.Minute * 10)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						time.Sleep(time.Minute)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					s.model.SendMessage(s.Name(), "main", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Type: "status",
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -179,6 +136,152 @@ func (s *AoCState) runLoop() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) ProcessDirectMessage(slackMsg slack.Message) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					msgPts := strings.Fields(slackMsg.Text)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(msgPts) < 2 || msgPts[0] != "!aoc" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					switch msgPts[1] {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "help":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.DoHelpCmd(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "ping":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.DoPingCmd(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "top":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.DoTopCmd(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) ProcessAdminDirectMessage(slackMsg slack.Message) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					msgPts := strings.Fields(slackMsg.Text)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(msgPts) < 2 || msgPts[0] != "!aoc" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					switch msgPts[1] {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "help":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.DoHelpAdminCmd(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "ping":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.DoPingCmd(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "top":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.DoTopCmd(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "session":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.DoSessionCmd(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) ProcessChannelMessage(slackMsg slack.Message) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					msgPts := strings.Fields(slackMsg.Text)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(msgPts) < 2 || msgPts[0] != "!aoc" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					switch msgPts[1] {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					case "top":
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.DoTopCmd(slackMsg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) DoHelpCmd(slackMsg slack.Message) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					txt := fmt.Sprint(":christmas_tree: AoC Help :christmas_tree:\n",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						"-- WiP --",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Channel: slackMsg.Channel,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Text:    txt,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) DoHelpAdminCmd(slackMsg slack.Message) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					txt := fmt.Sprint(":christmas_tree: AoC Help :christmas_tree:\n",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						"-- WiP --",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Channel: slackMsg.Channel,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Text:    txt,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) DoPingCmd(slackMsg slack.Message) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Channel: slackMsg.Channel,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Text:    ":christmas_tree: PONG :christmas_tree:",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) DoSessionCmd(slackMsg slack.Message) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					msgPts := strings.Fields(slackMsg.Text)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(msgPts) == 3 && msgPts[1] == "session" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						// Set the session cookie
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						admin, err := s.model.GetSlackAdminDMId()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							s.model.SendMessage(s.Name(), "error", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Type: "error",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Text: "Error getting Admin DM Id",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Time: time.Now(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						aocSession := msgPts[2]
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.setAoCSessionCookie(strings.TrimSpace(aocSession))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.sessionCookie = aocSession
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.sessionNeedsUpdate = false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Channel: admin,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Text:    ":christmas_tree: New Session: " + s.sessionCookie,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else if len(msgPts) == 2 && msgPts[1] == "session" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						// Print the session cookie
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						admin, err := s.model.GetSlackAdminDMId()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							s.model.SendMessage(s.Name(), "error", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Type: "error",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Text: "Error getting Admin DM Id",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Time: time.Now(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						// We only send the session cookie to the admin
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Channel: admin,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Text:    ":christmas_tree: session: " + s.sessionCookie,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) DoTopCmd(slackMsg slack.Message) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					msgPts := strings.Fields(slackMsg.Text)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					var err error
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					var yr int
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if len(msgPts) > 2 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						yr, err = strconv.Atoi(msgPts[2])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if msgPts[2] == "all" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								yr = -1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						yr = s.GetLatestYear()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					var txt string
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if yr == -1 {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						var err error
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						txt, err = s.DoTopForAll()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							txt = "Error calculating all-time tops"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						txt = s.DoTopForYear(yr)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Channel: slackMsg.Channel,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						Text:    txt,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) DoTopForAll() (string, error) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					mbrMap := make(map[string]aoc.Member)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					for _, yr := range s.GetListOfAoCYears() {
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -269,6 +372,74 @@ func (s *AoCState) GetAoCBoard(yr int) (*aoc.Leaderboard, error) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return s.aoc.GetLeaderboard(yr)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) AoCBoardCheckAndUpdate(yr int) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					channelId, _ := s.getChannelId()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if s.AoCBoardNeedsUpdate(yr) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						l, err := s.aoc.GetLeaderboard(yr)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							admin, adminErr := s.model.GetSlackAdminDMId()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if adminErr != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.model.SendMessage(s.Name(), "error", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Type: "error",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Text: "Error getting Admin DM Id",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Time: time.Now(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if err.Error() == "Invalid Session Cookie" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.sessionNeedsUpdate = true
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Channel: admin,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Text:    fmt.Sprintf(":warning: AoC Error processing leaderboard (%d) - %s", yr, err.Error()),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						msg := slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Type: "success",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Text: fmt.Sprintf("Received leaderboard (%d)", yr),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							Time: time.Now(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.model.SendMessage(s.Name(), "main", msg)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						// Compare the new leaderboard to the saved one
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						for _, v := range l.Members {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							mbr, err := s.getMember(l.Event, v.ID)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								continue
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							if mbr.Stars != v.Stars {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								s.model.SendMessage(s.Name(), "slack", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Type:    "message",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Channel: channelId,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
									Text:    ":christmas_tree: " + v.Name + " now has " + strconv.Itoa(v.Stars) + " stars! :christmas_tree:",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								})
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						// Save the leaderboard to the db
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						s.saveLeaderboard(l)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) AoCBoardNeedsUpdate(yr int) bool {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					var freshDt time.Duration
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if yr == s.GetLatestYear() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						freshDt, _ = time.ParseDuration("10m")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					} else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						freshDt, _ = time.ParseDuration("1h")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					l, err := s.aoc.GetCachedLeaderboard(yr)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					if err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err.Error() == "Invalid Year" {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							return false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						// This board is not cached, we need to pull it
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						return true
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					return time.Since(l.LastFetch) > freshDt
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				func (s *AoCState) RequestBoardId() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					reader := bufio.NewReader(os.Stdin)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					fmt.Print("Advent of Code Board ID: ")
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -281,6 +452,7 @@ func (s *AoCState) RequestSessionCookie() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					fmt.Print("Advent of Code Session Cookie: ")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					aocSession, _ := reader.ReadString('\n')
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					s.setAoCSessionCookie(strings.TrimSpace(aocSession))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					s.sessionCookie = aocSession
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// The channel that we post updates to
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -327,7 +499,7 @@ func (s *AoCState) saveLeaderboard(l *aoc.Leaderboard) error {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
					for _, v := range l.Members {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
						if err = s.saveMember(l.Event, &v); err != nil {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							s.model.SendMessage(s.Name(), "main", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
							s.model.SendMessage(s.Name(), "error", slack.Message{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Type: "error",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Text: fmt.Sprintf("Error Saving Member (%s)", v.Name),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
								Time: time.Now(),
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				 
 |