Starting on Achievements

A few cleanup things too.
This commit is contained in:
Brian Buller 2015-11-05 19:36:09 -06:00
parent 278d409ad9
commit 0b202e63fa
8 changed files with 351 additions and 89 deletions

View File

@ -38,3 +38,31 @@ func getAllLevelUpChannelXp(user string) map[string]int {
closeDatabase()
return ret
}
func getAllNonLevelUpStats(user string) map[string]int {
openDatabase()
ret := make(map[string]int)
// First, get a list of all levelup stats
db.Update(func(tx *bolt.Tx) error {
var b, uB, uSB *bolt.Bucket
var err error
b = tx.Bucket([]byte("users"))
if b == nil {
return fmt.Errorf("Unable to open 'users' bucket")
}
if uB = b.Bucket([]byte(user)); uB != nil {
if uSB = uB.Bucket([]byte("stats")); uSB != nil {
return uSB.ForEach(func(k, v []byte) error {
if !strings.HasPrefix(string(k), "levelup-") {
ret[string(k)], _ = strconv.Atoi(string(v))
}
return nil
})
}
}
return err
})
closeDatabase()
return ret
}

View File

@ -275,41 +275,45 @@ func (wm *generalWebModule) handleStats(w http.ResponseWriter, req *http.Request
sc := "var stats = {totalchannelmessages:"
sc = fmt.Sprintf("%s%d,", sc, s.TotalChannelMessages)
sc = sc + "channels:["
for _, k := range s.ChannelStats {
sc = fmt.Sprintf("%s{name:\"%s\",member_count:%d,message_count:%d},",
sc,
k.Name,
k.MemberCount,
k.MessageCount,
)
if len(s.ChannelStats) > 0 {
for _, k := range s.ChannelStats {
sc = fmt.Sprintf("%s{name:\"%s\",member_count:%d,message_count:%d},",
sc,
k.Name,
k.MemberCount,
k.MessageCount,
)
}
// Trim the last ,
sc = sc[:len(sc)-1]
}
// Trim the last ,
sc = sc[:len(sc)-1]
sc = sc + "],"
sc = sc + "users:["
for _, usr := range s.UserStats {
sc = fmt.Sprintf("%s{name:\"%s\",message_count:%d,",
sc,
usr.Name,
usr.Messages,
)
sc = sc + "messages:{"
sc = sc + "hours:{"
for i, k := range usr.Hours {
sc = sc + fmt.Sprintf("%d:%d,", i, k)
if len(s.UserStats) > 0 {
for _, usr := range s.UserStats {
sc = fmt.Sprintf("%s{name:\"%s\",message_count:%d,",
sc,
usr.Name,
usr.Messages,
)
sc = sc + "messages:{"
sc = sc + "hours:{"
for i, k := range usr.Hours {
sc = sc + fmt.Sprintf("%d:%d,", i, k)
}
sc = sc[:len(sc)-1]
sc = sc + "},"
sc = sc + "dow:{"
for _, k := range []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} {
sc = sc + fmt.Sprintf("%s:%d,", k, usr.Dow[k])
}
sc = sc[:len(sc)-1]
sc = sc + "}}"
sc = sc + "},"
}
// Trim the last ,
sc = sc[:len(sc)-1]
sc = sc + "},"
sc = sc + "dow:{"
for _, k := range []string{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} {
sc = sc + fmt.Sprintf("%s:%d,", k, usr.Dow[k])
}
sc = sc[:len(sc)-1]
sc = sc + "}}"
sc = sc + "},"
}
// Trim the last ,
sc = sc[:len(sc)-1]
sc = sc + "],"
// Get all of the message stats
sc = sc + "messages:{"

View File

@ -3,6 +3,8 @@ package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
type levelUpStatProcessor struct{}
@ -56,6 +58,7 @@ func (wm *levelUpWebModule) GetName() string {
func (wm *levelUpWebModule) GetRoutes() map[string]func(http.ResponseWriter, *http.Request) {
ret := make(map[string]func(http.ResponseWriter, *http.Request))
ret["/levelup/"] = wm.handleLevelUpGeneral
ret["/levelup/profile/{username}"] = wm.handleLevelUpProfile
return ret
}
func (wm *levelUpWebModule) Register() {
@ -106,23 +109,49 @@ func (wm *levelUpWebModule) handleLevelUpGeneral(w http.ResponseWriter, req *htt
sc := "var levelUpStats = {"
sc = sc + "users:["
for _, k := range userStats {
sc = sc + "{"
sc = sc + "name:\"" + k.Name + "\","
sc = fmt.Sprintf("%sxp:%d,", sc, k.Xp)
sc = sc + "channels:["
if len(k.ChannelStats) > 0 {
for chK, chV := range k.ChannelStats {
sc = fmt.Sprintf("%s{name:\"%s\",xp:%d},", sc, chK, chV)
if len(userStats) > 0 {
for _, k := range userStats {
sc = sc + "{"
sc = sc + "name:\"" + k.Name + "\","
sc = fmt.Sprintf("%sxp:%d,", sc, k.Xp)
sc = sc + "channels:["
if len(k.ChannelStats) > 0 {
for chK, chV := range k.ChannelStats {
sc = fmt.Sprintf("%s{name:\"%s\",xp:%d},", sc, chK, chV)
}
sc = sc[:len(sc)-1]
}
sc = sc[:len(sc)-1]
sc = sc + "]},"
}
sc = sc + "]},"
sc = sc[:len(sc)-1]
}
sc = sc[:len(sc)-1]
sc = sc + "]};"
addToInlineScript(sc)
site.Scripts = append(site.Scripts, "/assets/js/levelup_main.js")
showPage("levelup-main.html", site, w)
}
func (wm *levelUpWebModule) handleLevelUpProfile(w http.ResponseWriter, req *http.Request) {
type UserLevelUpStats struct {
Name string
Xp int
ChannelStats map[string]int
OtherStats map[string]int
}
var u *User
var err error
vars := mux.Vars(req)
if u, err = getUserInfoFromName(vars["username"]); err != nil {
pageNotFound(fmt.Errorf(fmt.Sprintf("Couldn't find a profile for user "+vars["username"])+"\n%s\n", err), w)
return
}
p := UserLevelUpStats{Name: vars["username"]}
p.Xp, _ = getUserStat(u.ID, "levelup-xp")
p.ChannelStats = getAllLevelUpChannelXp(u.ID)
p.OtherStats = getAllNonLevelUpStats(u.ID)
site.TemplateData = p
initRequest(w, req)
showPage("levelup-profile.html", site, w)
}

View File

@ -0,0 +1,95 @@
package main
import (
"net/http"
"github.com/gorilla/mux"
)
type levelUpAchieveStatProcessor struct {
Achievements []levelUpAchievement
}
func (p *levelUpAchieveStatProcessor) GetName() string {
return "LevelUp Achievements"
}
func (p *levelUpAchieveStatProcessor) GetStatKeys() []string {
return []string{
"levelupachievement-*",
}
}
type levelUpAchievement interface {
GetName() string
GetText() string
// Returns whether the user already has this achievement
DoesUserHave(uID string) bool
// Processes the message, returns true if the achievement was triggered
ProcessMessage(m *Message) bool
}
func (p *levelUpAchieveStatProcessor) ProcessMessage(m *Message) {
type UserLevelUpStats struct {
Name string
Xp int
ChannelStats map[string]int
OtherStats map[string]int
}
var u *User
var err error
vars := mux.Vars(req)
if u, err = getUserInfo(m.User); err != nil {
return
}
p := UserLevelUpStats{Name: u.Name}
p.Xp, _ = getUserStat(u.ID, "levelup-xp")
p.ChannelStats = getAllLevelUpChannelXp(u.ID)
p.OtherStats = getAllNonLevelUpStats(u.ID)
}
func (p *levelUpAchieveStatProcessor) ProcessAdminMessage(m *Message) {}
func (p *levelUpAchieveStatProcessor) ProcessBotMessage(m *Message) {}
func (p *levelUpAchieveStatProcessor) ProcessUserMessage(m *Message) {}
func (p *levelUpAchieveStatProcessor) ProcessAdminUserMessage(m *Message) {}
func (p *levelUpAchieveStatProcessor) ProcessBotUserMessage(m *Message) {}
func (p *levelUpAchieveStatProcessor) ProcessChannelMessage(m *Message) {
}
func (p *levelUpAchieveStatProcessor) ProcessAdminChannelMessage(m *Message) {}
func (p *levelUpAchieveStatProcessor) ProcessBotChannelMessage(m *Message) {}
/*
* Web Site Module
*/
type levelUpAchieveWebModule struct{}
func (wm *levelUpAchieveWebModule) GetName() string {
return "LevelUp Achievement Web Module"
}
func (wm *levelUpAchieveWebModule) GetRoutes() map[string]func(http.ResponseWriter, *http.Request) {
ret := make(map[string]func(http.ResponseWriter, *http.Request))
ret["/levelup/achieve"] = wm.handleLevelUpAchieveGeneral
return ret
}
func (wm *levelUpAchieveWebModule) Register() {
for k, v := range wm.GetRoutes() {
r.HandleFunc(k, v)
}
}
func (wm *levelUpAchieveWebModule) GetMenuEntries() []menuItem {
var ret []menuItem
ret = append(ret, menuItem{Text: "Achieve GET!", Link: "/levelup/achieve"})
return ret
}
func (wm *levelUpAchieveWebModule) GetBottomMenuEntries() []menuItem {
var ret []menuItem
return ret
}
func (wm *levelUpAchieveWebModule) handleLevelUpAchieveGeneral(w http.ResponseWriter, req *http.Request) {
initRequest(w, req)
setMenuItemActive("Achieve GET!")
}

View File

@ -65,6 +65,9 @@ func statBotMain(slack *Slack) {
registerStatProcessor(new(levelUpStatProcessor))
registerStatProcessor(new(generalStatProcessor))
levelUpAchievements := new(levelUpAchieveStatProcessor)
// Register Achievements
registerMessageProcessor(new(generalProcessor))
fmt.Println("statbot ready, ^C exits")
@ -74,6 +77,7 @@ func statBotMain(slack *Slack) {
// read each incoming message
m, err := slack.getMessage()
if err == nil {
writeToLog(" " + time.Now().Format(time.RFC3339) + " - Received Message\n")
processMessage(slack, &m)
}
}

View File

@ -553,54 +553,56 @@ func getUserInfo(usr string) (*User, error) {
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("users"))
if uB := b.Bucket([]byte(usr)); uB != nil {
var err error
if ret.Name, err = bktGetString(uB, "name"); err != nil {
return err
}
if ret.Deleted, err = bktGetBool(uB, "deleted"); err != nil {
return err
}
if ret.Status, err = bktGetString(uB, "status"); err != nil {
return err
}
if ret.Color, err = bktGetString(uB, "color"); err != nil {
return err
}
if ret.RealName, err = bktGetString(uB, "real_name"); err != nil {
return err
}
if ret.TZ, err = bktGetString(uB, "tz"); err != nil {
return err
}
if ret.TZLabel, err = bktGetString(uB, "tz_label"); err != nil {
return err
}
if ret.TZOffset, err = bktGetInt(uB, "tz_offset"); err != nil {
return err
}
if ret.IsAdmin, err = bktGetBool(uB, "is_admin"); err != nil {
return err
}
if ret.IsOwner, err = bktGetBool(uB, "is_owner"); err != nil {
return err
}
if ret.IsPrimaryOwner, err = bktGetBool(uB, "is_primary_owner"); err != nil {
return err
}
if ret.IsRestricted, err = bktGetBool(uB, "is_restricted"); err != nil {
return err
}
if ret.IsUltraRestricted, err = bktGetBool(uB, "is_ultra_restricted"); err != nil {
return err
}
if ret.IsBot, err = bktGetBool(uB, "is_bot"); err != nil {
return err
}
if ret.HasFiles, err = bktGetBool(uB, "has_files"); err != nil {
return err
}
if ret.LastUpdated, err = bktGetTime(uB, "last_updated"); err != nil {
return err
if uIB := uB.Bucket([]byte("info")); uIB != nil {
var err error
if ret.Name, err = bktGetString(uIB, "name"); err != nil {
return err
}
if ret.Deleted, err = bktGetBool(uIB, "deleted"); err != nil {
return err
}
if ret.Status, err = bktGetString(uIB, "status"); err != nil {
return err
}
if ret.Color, err = bktGetString(uIB, "color"); err != nil {
return err
}
if ret.RealName, err = bktGetString(uIB, "real_name"); err != nil {
return err
}
if ret.TZ, err = bktGetString(uIB, "tz"); err != nil {
return err
}
if ret.TZLabel, err = bktGetString(uIB, "tz_label"); err != nil {
return err
}
if ret.TZOffset, err = bktGetInt(uIB, "tz_offset"); err != nil {
return err
}
if ret.IsAdmin, err = bktGetBool(uIB, "is_admin"); err != nil {
return err
}
if ret.IsOwner, err = bktGetBool(uIB, "is_owner"); err != nil {
return err
}
if ret.IsPrimaryOwner, err = bktGetBool(uIB, "is_primary_owner"); err != nil {
return err
}
if ret.IsRestricted, err = bktGetBool(uIB, "is_restricted"); err != nil {
return err
}
if ret.IsUltraRestricted, err = bktGetBool(uIB, "is_ultra_restricted"); err != nil {
return err
}
if ret.IsBot, err = bktGetBool(uIB, "is_bot"); err != nil {
return err
}
if ret.HasFiles, err = bktGetBool(uIB, "has_files"); err != nil {
return err
}
if ret.LastUpdated, err = bktGetTime(uB, "last_updated"); err != nil {
return err
}
}
return nil
}
@ -610,6 +612,84 @@ func getUserInfo(usr string) (*User, error) {
return &ret, err
}
func getUserInfoFromName(usrName string) (*User, error) {
openDatabase()
ret := User{}
err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("users"))
c := b.Cursor()
var tstName string
var uB, uIB *bolt.Bucket
var err error
var userID string
for k, v := c.First(); k != nil && strings.ToLower(tstName) != strings.ToLower(usrName); k, v = c.Next() {
if v == nil {
if uB = b.Bucket(k); uB != nil {
if uIB = uB.Bucket([]byte("info")); uIB != nil {
if tstName, err = bktGetString(uIB, "name"); err != nil {
return err
}
}
userID = string(k)
}
}
}
if tstName != usrName {
return fmt.Errorf("Requested user (" + usrName + ") not found")
}
ret.ID = userID
ret.Name = tstName
if ret.Deleted, err = bktGetBool(uIB, "deleted"); err != nil {
return err
}
if ret.Status, err = bktGetString(uIB, "status"); err != nil {
return err
}
if ret.Color, err = bktGetString(uIB, "color"); err != nil {
return err
}
if ret.RealName, err = bktGetString(uIB, "real_name"); err != nil {
return err
}
if ret.TZ, err = bktGetString(uIB, "tz"); err != nil {
return err
}
if ret.TZLabel, err = bktGetString(uIB, "tz_label"); err != nil {
return err
}
if ret.TZOffset, err = bktGetInt(uIB, "tz_offset"); err != nil {
return err
}
if ret.IsAdmin, err = bktGetBool(uIB, "is_admin"); err != nil {
return err
}
if ret.IsOwner, err = bktGetBool(uIB, "is_owner"); err != nil {
return err
}
if ret.IsPrimaryOwner, err = bktGetBool(uIB, "is_primary_owner"); err != nil {
return err
}
if ret.IsRestricted, err = bktGetBool(uIB, "is_restricted"); err != nil {
return err
}
if ret.IsUltraRestricted, err = bktGetBool(uIB, "is_ultra_restricted"); err != nil {
return err
}
if ret.IsBot, err = bktGetBool(uIB, "is_bot"); err != nil {
return err
}
if ret.HasFiles, err = bktGetBool(uIB, "has_files"); err != nil {
return err
}
if ret.LastUpdated, err = bktGetTime(uIB, "last_updated"); err != nil {
return err
}
return err
})
closeDatabase()
return &ret, err
}
func saveUserInfo(usr *User) error {
openDatabase()
err := db.Update(func(tx *bolt.Tx) error {
@ -670,7 +750,7 @@ func saveUserInfo(usr *User) error {
if err = bktPutBool(uIB, "has_files", usr.HasFiles); err != nil {
return err
}
err = bktPutTime(uIB, "last_udpated", usr.LastUpdated)
err = bktPutTime(uIB, "last_updated", usr.LastUpdated)
return err
})
closeDatabase()

View File

@ -6,6 +6,7 @@ import (
"net/http"
"os"
"text/template"
"time"
"github.com/gorilla/context"
"github.com/gorilla/mux"
@ -72,6 +73,7 @@ func statWebMain(slack *Slack) {
registerWebModule(new(generalWebModule))
registerWebModule(new(levelUpWebModule))
registerWebModule(new(levelUpAchieveWebModule))
http.Handle("/", r)
go func() {
@ -80,6 +82,7 @@ func statWebMain(slack *Slack) {
}
func initRequest(w http.ResponseWriter, req *http.Request) {
writeToLog(" " + time.Now().Format(time.RFC3339) + " >> Web Request Received\n")
site.SubTitle = ""
site.Stylesheets = make([]string, 0, 0)
site.Stylesheets = append(site.Stylesheets, "/assets/css/pure-min.css")
@ -190,6 +193,13 @@ func addToInlineScript(s string) {
site.InlineScript = fmt.Sprintf("%s%s", site.InlineScript, s)
}
func pageNotFound(err error, w http.ResponseWriter) {
if err == nil {
err = fmt.Errorf("Page Not Found")
}
http.Error(w, err.Error(), 404)
}
func assertError(err error, w http.ResponseWriter) bool {
if err != nil {
http.Error(w, err.Error(), 500)

View File

@ -0,0 +1,12 @@
<h1>{{ .TemplateData.Name }}</h1>
<div>{{ .TemplateData.Xp }}</div>
<ul>
{{ range $i, $v := .TemplateData.ChannelStats }}
<li>{{ $i }} - {{ $v }}</li>
{{ end }}
</ul>
<ul>
{{ range $i, $v := .TemplateData.OtherStats }}
<li>{{ $i }} - {{ $v }}</li>
{{ end }}
</ul>