2015-10-28 16:46:45 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2015-10-29 19:16:45 +00:00
|
|
|
"strings"
|
2015-10-28 16:46:45 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/boltdb/bolt"
|
|
|
|
)
|
|
|
|
|
|
|
|
const databaseFile = programName + ".db"
|
|
|
|
|
|
|
|
var db *bolt.DB
|
|
|
|
|
2015-10-29 16:16:25 +00:00
|
|
|
var dbOpened bool
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
func openDatabase() error {
|
2015-10-29 16:16:25 +00:00
|
|
|
if !dbOpened {
|
|
|
|
var err error
|
|
|
|
db, err = bolt.Open(databaseFile, 0600, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
dbOpened = true
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func closeDatabase() error {
|
2015-10-29 16:16:25 +00:00
|
|
|
if !dbOpened {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
dbOpened = false
|
2015-10-28 16:46:45 +00:00
|
|
|
return db.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func initDatabase() error {
|
|
|
|
openDatabase()
|
|
|
|
|
|
|
|
// Make sure the needed buckets exists
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
|
|
|
|
// The config bucket holds config info for statbot
|
2015-10-29 16:16:25 +00:00
|
|
|
cB, err := tx.CreateBucketIfNotExists([]byte("config"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = cB.CreateBucketIfNotExists([]byte("admins"))
|
2015-10-28 16:46:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// The 'users' bucket holds user info, including direct messages
|
|
|
|
_, err = tx.CreateBucketIfNotExists([]byte("users"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// The 'channels' bucket holds channel info, including channel messages
|
|
|
|
_, err = tx.CreateBucketIfNotExists([]byte("channels"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-10-29 19:16:45 +00:00
|
|
|
func isBot(uid string) (bool, error) {
|
|
|
|
openDatabase()
|
|
|
|
var ret bool
|
|
|
|
err := db.View(func(tx *bolt.Tx) error {
|
|
|
|
var b, uB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
b = tx.Bucket([]byte("users"))
|
|
|
|
if b == nil {
|
|
|
|
return fmt.Errorf("Unable to open 'users' bucket")
|
|
|
|
}
|
|
|
|
uB = b.Bucket([]byte(uid))
|
|
|
|
ret, err = bktGetBool(uB, "is_bot")
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
return ret, err
|
|
|
|
}
|
|
|
|
|
2015-10-29 16:16:25 +00:00
|
|
|
func isAdmin(uid string) bool {
|
|
|
|
var foundUser bool
|
|
|
|
openDatabase()
|
|
|
|
err := db.View(func(tx *bolt.Tx) error {
|
|
|
|
var cB, caB *bolt.Bucket
|
|
|
|
cB = tx.Bucket([]byte("config"))
|
|
|
|
if cB == nil {
|
|
|
|
return fmt.Errorf("Error opening 'config' bucket")
|
|
|
|
}
|
|
|
|
if caB = cB.Bucket([]byte("admins")); caB == nil {
|
|
|
|
return fmt.Errorf("Error opening 'config/admins' bucket")
|
|
|
|
}
|
|
|
|
_, err := bktGetInt(caB, uid)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
foundUser = err == nil
|
|
|
|
return foundUser
|
|
|
|
}
|
|
|
|
|
|
|
|
func addAdmin(uid string) error {
|
|
|
|
if !isAdmin(uid) {
|
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
var err error
|
|
|
|
var cB, caB *bolt.Bucket
|
|
|
|
cB = tx.Bucket([]byte("config"))
|
|
|
|
if cB == nil {
|
|
|
|
return fmt.Errorf("Error opening 'config' bucket")
|
|
|
|
}
|
|
|
|
if caB = cB.Bucket([]byte("admins")); caB == nil {
|
|
|
|
return fmt.Errorf("Error opening 'config/admins' bucket")
|
|
|
|
}
|
|
|
|
err = bktPutInt(caB, uid, 1)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return fmt.Errorf("User is already an admin")
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeAdmin(uid string) error {
|
|
|
|
if isAdmin(uid) {
|
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
var err error
|
|
|
|
var cB, caB *bolt.Bucket
|
|
|
|
cB = tx.Bucket([]byte("config"))
|
|
|
|
if cB == nil {
|
|
|
|
return fmt.Errorf("Error opening 'config' bucket")
|
|
|
|
}
|
|
|
|
if caB = cB.Bucket([]byte("admins")); caB == nil {
|
|
|
|
return fmt.Errorf("Error opening 'config/admins' bucket")
|
|
|
|
}
|
|
|
|
// Find the admin level for the user to be removed
|
|
|
|
// (assume it's a normal admin)
|
|
|
|
adminLevel := 1
|
|
|
|
if adminLevel, err = bktGetInt(caB, uid); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if adminLevel > 0 {
|
|
|
|
return caB.Delete([]byte(uid))
|
|
|
|
}
|
|
|
|
return fmt.Errorf("Unable to remove privileges. User's admin level is too high.")
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return fmt.Errorf("User is not an admin")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Message We save the message struct into the db,
|
|
|
|
// it's also what we send/receive from slack
|
|
|
|
type Message struct {
|
|
|
|
ID uint64 `json:"id"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
Channel string `json:"channel"`
|
|
|
|
User string `json:"user"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Text string `json:"text"`
|
|
|
|
Ts string `json:"ts"`
|
|
|
|
Time time.Time
|
|
|
|
}
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
// Channel object
|
|
|
|
type Channel struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
IsChannel bool `json:"is_channel"`
|
|
|
|
CreatedRaw int `json:"created"`
|
|
|
|
Created time.Time
|
|
|
|
Creator string `json:"creator"`
|
|
|
|
IsArchived bool `json:"is_archived"`
|
|
|
|
IsGeneral bool `json:"is_general"`
|
|
|
|
IsMember bool `json:"is_member"`
|
2015-10-29 19:16:45 +00:00
|
|
|
LastReadRaw string `json:"last_read"`
|
2015-10-28 16:46:45 +00:00
|
|
|
LastRead time.Time
|
|
|
|
Latest *Message `json:"latest"`
|
|
|
|
UnreadCount int `json:"unread_count"`
|
|
|
|
UnreadCountDisplay int `json:"unread_count_display"`
|
|
|
|
Members []string `json:"members"`
|
|
|
|
Topic *ChannelTopic `json:"topic"`
|
|
|
|
Purpose *ChannelTopic `json:"purpose"`
|
|
|
|
|
|
|
|
// LastUpdated is the last time we updated the info in the DB
|
|
|
|
LastUpdated time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChannelTopic A simple 'Channel Topic' object
|
|
|
|
// used for several things in the slack api (channel topic/purpose, etc.
|
|
|
|
type ChannelTopic struct {
|
|
|
|
Value string `json:"value"`
|
|
|
|
Creator string `json:"creator"`
|
|
|
|
LastSetRaw int `json:"last_set"`
|
|
|
|
LastSet time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
func getChannelInfo(chnl string) (*Channel, error) {
|
|
|
|
openDatabase()
|
|
|
|
ret := Channel{ID: chnl}
|
|
|
|
err := db.View(func(tx *bolt.Tx) error {
|
|
|
|
var b, chB, chIB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
|
|
|
|
b = tx.Bucket([]byte("channels"))
|
|
|
|
if b == nil {
|
|
|
|
return fmt.Errorf("Error opening 'channels' bucket")
|
|
|
|
}
|
|
|
|
if chB = b.Bucket([]byte(chnl)); chB == nil {
|
|
|
|
return fmt.Errorf("Error opening channel bucket (%s)", chnl)
|
|
|
|
}
|
|
|
|
if chIB = chB.Bucket([]byte("info")); chIB == nil {
|
|
|
|
return fmt.Errorf("Error opening channel info bucket (%s/info)", chnl)
|
|
|
|
}
|
|
|
|
if ret.Name, err = bktGetString(chIB, "name"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.IsChannel, err = bktGetBool(chIB, "is_channel"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.Created, err = bktGetTime(chIB, "created"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.Creator, err = bktGetString(chIB, "creator"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.IsArchived, err = bktGetBool(chIB, "is_archived"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.IsGeneral, err = bktGetBool(chIB, "is_general"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.IsMember, err = bktGetBool(chIB, "is_member"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.LastRead, err = bktGetTime(chIB, "last_read"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.UnreadCount, err = bktGetInt(chIB, "unread_count"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.UnreadCountDisplay, err = bktGetInt(chIB, "unread_count_display"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Get all of the members into a []string
|
|
|
|
chMembersB := chIB.Bucket([]byte("members"))
|
|
|
|
if chMembersB != nil {
|
|
|
|
chMembersB.ForEach(func(k, v []byte) error {
|
|
|
|
ret.Members = append(ret.Members, string(v))
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
ret.Topic = new(ChannelTopic)
|
|
|
|
if ret.Topic.Value, err = bktGetString(chIB, "topic_value"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.Topic.Creator, err = bktGetString(chIB, "topic_creator"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.Topic.LastSet, err = bktGetTime(chIB, "topic_last_set"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ret.Purpose = new(ChannelTopic)
|
|
|
|
if ret.Purpose.Value, err = bktGetString(chIB, "purpose_value"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.Purpose.Creator, err = bktGetString(chIB, "purpose_creator"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.Purpose.LastSet, err = bktGetTime(chIB, "purpose_last_set"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return &ret, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func saveChannelInfo(chnl *Channel) error {
|
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
2015-10-29 16:16:25 +00:00
|
|
|
b := tx.Bucket([]byte("channels"))
|
2015-10-28 16:46:45 +00:00
|
|
|
var err error
|
|
|
|
var chB, chIB *bolt.Bucket
|
|
|
|
if chB, err = b.CreateBucketIfNotExists([]byte(chnl.ID)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if chIB, err = chB.CreateBucketIfNotExists([]byte("info")); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(chIB, "id", chnl.ID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(chIB, "name", chnl.Name); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(chIB, "is_channel", chnl.IsChannel); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutTime(chIB, "created", chnl.Created); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(chIB, "creator", chnl.Creator); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(chIB, "is_archived", chnl.IsArchived); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(chIB, "is_general", chnl.IsGeneral); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(chIB, "is_member", chnl.IsMember); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutTime(chIB, "last_read", chnl.LastRead); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutInt(chIB, "unread_count", chnl.UnreadCount); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutInt(chIB, "unread_count_display", chnl.UnreadCountDisplay); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
chMembersB := chIB.Bucket([]byte("members"))
|
|
|
|
if chMembersB != nil {
|
|
|
|
// Recreate the Members bucket
|
|
|
|
chIB.DeleteBucket([]byte("members"))
|
|
|
|
}
|
|
|
|
if chMembersB, err = chIB.CreateBucket([]byte("members")); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mbIdx := 0
|
|
|
|
for _, mm := range chnl.Members {
|
|
|
|
idxKey := strconv.Itoa(mbIdx)
|
|
|
|
if err = bktPutString(chMembersB, idxKey, mm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-10-29 19:16:45 +00:00
|
|
|
mbIdx++
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
|
|
|
if chnl.Topic != nil {
|
|
|
|
if err = bktPutString(chIB, "topic_value", chnl.Topic.Value); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(chIB, "topic_creator", chnl.Topic.Creator); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutTime(chIB, "topic_last_set", chnl.Topic.LastSet); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if chnl.Purpose != nil {
|
|
|
|
if err = bktPutString(chIB, "purpose_value", chnl.Topic.Value); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(chIB, "purpose_creator", chnl.Topic.Creator); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutTime(chIB, "purpose_last_set", chnl.Topic.LastSet); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = bktPutTime(chIB, "last_updated", chnl.LastUpdated)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-10-29 19:16:45 +00:00
|
|
|
func saveChannelStat(channel string, key string, val int) error {
|
2015-10-28 16:46:45 +00:00
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
2015-10-29 19:16:45 +00:00
|
|
|
var b, chB, chSB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
|
|
|
|
b = tx.Bucket([]byte("channels"))
|
|
|
|
if chB, err = b.CreateBucketIfNotExists([]byte(channel)); err == nil {
|
|
|
|
if chSB, err = chB.CreateBucketIfNotExists([]byte("stats")); err == nil {
|
|
|
|
err = bktPutInt(chSB, key, val)
|
|
|
|
}
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
2015-10-29 19:16:45 +00:00
|
|
|
return err
|
2015-10-28 16:46:45 +00:00
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-10-29 19:16:45 +00:00
|
|
|
func addChannelStat(channel string, key string, addVal int) error {
|
2015-10-28 16:46:45 +00:00
|
|
|
openDatabase()
|
2015-10-29 19:16:45 +00:00
|
|
|
v, err := getChannelStat(channel, key)
|
|
|
|
err = saveChannelStat(channel, key, v+addVal)
|
2015-10-28 16:46:45 +00:00
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-10-29 19:16:45 +00:00
|
|
|
func incrementChannelStat(channel string, key string) error {
|
|
|
|
return addChannelStat(channel, key, 1)
|
|
|
|
}
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
func decrementChannelStat(channel string, key string) error {
|
2015-10-29 19:16:45 +00:00
|
|
|
return addChannelStat(channel, key, -1)
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
|
|
|
|
2015-11-02 19:48:29 +00:00
|
|
|
func getChannelName(chnl string) string {
|
|
|
|
var ret string
|
|
|
|
openDatabase()
|
|
|
|
db.View(func(tx *bolt.Tx) error {
|
|
|
|
var b, chB, chIB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
|
|
|
|
b = tx.Bucket([]byte("channels"))
|
|
|
|
if b == nil {
|
|
|
|
return fmt.Errorf("Error opening 'channels' bucket")
|
|
|
|
}
|
|
|
|
if chB = b.Bucket([]byte(chnl)); chB == nil {
|
|
|
|
return fmt.Errorf("Error opening channel bucket (%s)", chnl)
|
|
|
|
}
|
|
|
|
if chIB = chB.Bucket([]byte("info")); chIB == nil {
|
|
|
|
return fmt.Errorf("Error opening channel info bucket (%s/info)", chnl)
|
|
|
|
}
|
|
|
|
ret, err = bktGetString(chIB, "name")
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func getChannelMemberCount(chnl string) int {
|
|
|
|
var ret int
|
|
|
|
openDatabase()
|
|
|
|
db.View(func(tx *bolt.Tx) error {
|
|
|
|
var b, chB, chIB *bolt.Bucket
|
|
|
|
|
|
|
|
b = tx.Bucket([]byte("channels"))
|
|
|
|
if b == nil {
|
|
|
|
return fmt.Errorf("Error opening 'channels' bucket")
|
|
|
|
}
|
|
|
|
if chB = b.Bucket([]byte(chnl)); chB == nil {
|
|
|
|
return fmt.Errorf("Error opening channel bucket (%s)", chnl)
|
|
|
|
}
|
|
|
|
if chIB = chB.Bucket([]byte("info")); chIB == nil {
|
|
|
|
return fmt.Errorf("Error opening channel info bucket (%s/info)", chnl)
|
|
|
|
}
|
|
|
|
// Get all of the members into a []string
|
|
|
|
chMembersB := chIB.Bucket([]byte("members"))
|
|
|
|
ret = chMembersB.Stats().KeyN
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2015-10-29 19:16:45 +00:00
|
|
|
func getChannelStat(channel string, key string) (int, error) {
|
2015-10-28 16:46:45 +00:00
|
|
|
openDatabase()
|
2015-10-29 19:16:45 +00:00
|
|
|
var ret int
|
2015-10-28 16:46:45 +00:00
|
|
|
err := db.View(func(tx *bolt.Tx) error {
|
2015-10-29 19:16:45 +00:00
|
|
|
var b, chB, chSB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
|
|
|
|
b = tx.Bucket([]byte("channels"))
|
|
|
|
if b == nil {
|
|
|
|
return fmt.Errorf("Unable to open 'channels' bucket")
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
2015-11-02 19:48:29 +00:00
|
|
|
if chB = b.Bucket([]byte(channel)); chB != nil {
|
|
|
|
if chSB = chB.Bucket([]byte("stats")); chSB != nil {
|
2015-10-29 19:16:45 +00:00
|
|
|
ret, err = bktGetInt(chSB, key)
|
|
|
|
return err
|
|
|
|
}
|
2015-11-05 12:55:49 +00:00
|
|
|
fmt.Println("Unable to find channel stats bucket: " + string(channel))
|
|
|
|
} else {
|
|
|
|
fmt.Println("Unable to find channel bucket: " + string(channel))
|
2015-10-29 19:16:45 +00:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return ret, err
|
|
|
|
}
|
|
|
|
|
2015-10-29 16:16:25 +00:00
|
|
|
func saveChannelMessage(channel string, message *Message) error {
|
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket([]byte("channels"))
|
|
|
|
var err error
|
|
|
|
var chB, chMB, msgBkt *bolt.Bucket
|
|
|
|
if chB, err = b.CreateBucketIfNotExists([]byte(channel)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if chMB, err = chB.CreateBucketIfNotExists([]byte("messages")); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
idx, _ := chMB.NextSequence()
|
|
|
|
idxKey := []byte(strconv.FormatUint(idx, 10))
|
|
|
|
if msgBkt, err = chMB.CreateBucketIfNotExists(idxKey); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(msgBkt, "type", message.Type); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(msgBkt, "user", message.User); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(msgBkt, "text", message.Text); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(msgBkt, "name", message.Name); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutTime(msgBkt, "time", message.Time); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return err
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// User object
|
|
|
|
type User struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Deleted bool `json:"deleted"`
|
|
|
|
Status string `json:"status"`
|
|
|
|
Color string `json:"color"`
|
|
|
|
RealName string `json:"real_name"`
|
|
|
|
TZ string `json:"tz"`
|
|
|
|
TZLabel string `json:"tz_label"`
|
|
|
|
TZOffset int `json:"tz_offset"`
|
|
|
|
IsAdmin bool `json:"is_admin"`
|
|
|
|
IsOwner bool `json:"is_owner"`
|
|
|
|
IsPrimaryOwner bool `json:"is_primary_owner"`
|
|
|
|
IsRestricted bool `json:"is_restricted"`
|
|
|
|
IsUltraRestricted bool `json:"is_ultra_restricted"`
|
|
|
|
IsBot bool `json:"is_bot"`
|
|
|
|
HasFiles bool `json:"has_files"`
|
|
|
|
|
|
|
|
// LastUpdated is the last time we updated the info in the DB
|
|
|
|
LastUpdated time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
func getUserInfo(usr string) (*User, error) {
|
|
|
|
openDatabase()
|
|
|
|
ret := User{ID: usr}
|
|
|
|
err := db.View(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket([]byte("users"))
|
|
|
|
if uB := b.Bucket([]byte(usr)); uB != nil {
|
2015-11-06 01:36:09 +00:00
|
|
|
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
|
|
|
|
}
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return fmt.Errorf("User (%s) bucket not found.", usr)
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return &ret, err
|
|
|
|
}
|
|
|
|
|
2015-11-06 01:36:09 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
func saveUserInfo(usr *User) error {
|
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket([]byte("users"))
|
|
|
|
var err error
|
|
|
|
var uB, uIB *bolt.Bucket
|
|
|
|
if uB, err = b.CreateBucketIfNotExists([]byte(usr.ID)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if uIB, err = uB.CreateBucketIfNotExists([]byte("info")); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(uIB, "id", usr.ID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(uIB, "name", usr.Name); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(uIB, "deleted", usr.Deleted); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(uIB, "status", usr.Status); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(uIB, "color", usr.Color); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(uIB, "real_name", usr.RealName); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(uIB, "tz", usr.TZ); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(uIB, "tz_label", usr.TZLabel); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutInt(uIB, "tz_offset", usr.TZOffset); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(uIB, "is_admin", usr.IsAdmin); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(uIB, "is_owner", usr.IsOwner); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(uIB, "is_primary_owner", usr.IsPrimaryOwner); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(uIB, "is_restricted", usr.IsRestricted); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(uIB, "is_ultra_restricted", usr.IsUltraRestricted); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(uIB, "is_bot", usr.IsBot); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutBool(uIB, "has_files", usr.HasFiles); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-11-06 01:36:09 +00:00
|
|
|
err = bktPutTime(uIB, "last_updated", usr.LastUpdated)
|
2015-10-28 16:46:45 +00:00
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func saveUserMessage(user string, message *Message) error {
|
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket([]byte("users"))
|
|
|
|
var err error
|
|
|
|
var uB, uMB, msgBkt *bolt.Bucket
|
|
|
|
if uB, err = b.CreateBucketIfNotExists([]byte(user)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if uMB, err = uB.CreateBucketIfNotExists([]byte("messages")); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
idx, _ := uMB.NextSequence()
|
|
|
|
idxKey := []byte(strconv.FormatUint(idx, 10))
|
|
|
|
if msgBkt, err = uMB.CreateBucketIfNotExists(idxKey); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(msgBkt, "type", message.Type); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(msgBkt, "text", message.Text); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutString(msgBkt, "name", message.Name); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = bktPutTime(msgBkt, "time", message.Time); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-10-29 16:16:25 +00:00
|
|
|
func getUserStat(user string, key string) (int, error) {
|
|
|
|
openDatabase()
|
|
|
|
var ret int
|
2015-10-29 19:16:45 +00:00
|
|
|
err := db.View(func(tx *bolt.Tx) error {
|
2015-10-29 16:16:25 +00:00
|
|
|
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")
|
|
|
|
}
|
2015-11-02 19:48:29 +00:00
|
|
|
if uB = b.Bucket([]byte(user)); uB != nil {
|
|
|
|
if uSB = uB.Bucket([]byte("stats")); uSB != nil {
|
2015-10-29 16:16:25 +00:00
|
|
|
ret, err = bktGetInt(uSB, key)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return ret, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func saveUserStat(user string, key string, val int) error {
|
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
var b, uB, uSB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
|
|
|
|
b = tx.Bucket([]byte("users"))
|
|
|
|
if uB, err = b.CreateBucketIfNotExists([]byte(user)); err == nil {
|
|
|
|
if uSB, err = uB.CreateBucketIfNotExists([]byte("stats")); err == nil {
|
2015-10-29 19:16:45 +00:00
|
|
|
err = bktPutInt(uSB, key, val)
|
2015-10-29 16:16:25 +00:00
|
|
|
}
|
|
|
|
}
|
2015-10-29 19:16:45 +00:00
|
|
|
return err
|
2015-10-29 16:16:25 +00:00
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func addUserStat(user string, key string, addVal int) error {
|
|
|
|
openDatabase()
|
|
|
|
v, err := getUserStat(user, key)
|
|
|
|
err = saveUserStat(user, key, v+addVal)
|
2015-11-02 19:48:29 +00:00
|
|
|
v, err = getUserStat(user, key)
|
2015-10-29 16:16:25 +00:00
|
|
|
closeDatabase()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func incrementUserStat(user string, key string) error {
|
|
|
|
return addUserStat(user, key, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func decrementUserStat(user string, key string) error {
|
|
|
|
return addUserStat(user, key, -1)
|
|
|
|
}
|
|
|
|
|
2015-10-30 14:33:18 +00:00
|
|
|
func getAllUserStats(user string) (map[string]int, error) {
|
|
|
|
ret := make(map[string]int)
|
2015-10-30 13:10:05 +00:00
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
var b, uB, uSB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
b = tx.Bucket([]byte("users"))
|
|
|
|
if uB, err = b.CreateBucketIfNotExists([]byte(user)); err == nil {
|
|
|
|
if uSB, err = uB.CreateBucketIfNotExists([]byte("stats")); err == nil {
|
2015-10-30 14:33:18 +00:00
|
|
|
var st int
|
|
|
|
var key string
|
2015-10-30 13:10:05 +00:00
|
|
|
uSB.ForEach(func(k, v []byte) error {
|
2015-10-30 14:33:18 +00:00
|
|
|
key = string(k)
|
|
|
|
if st, err = bktGetInt(uSB, key); err == nil {
|
|
|
|
ret[key] = ret[key] + st
|
2015-10-30 13:10:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
2015-10-30 14:33:18 +00:00
|
|
|
return ret, err
|
2015-10-30 13:10:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getAllUsersStats() (map[string]int, error) {
|
2015-10-30 14:33:18 +00:00
|
|
|
ret := make(map[string]int)
|
2015-10-30 13:10:05 +00:00
|
|
|
openDatabase()
|
|
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
|
|
var b, uB, uSB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
b = tx.Bucket([]byte("users"))
|
|
|
|
err = b.ForEach(func(k, v []byte) error {
|
|
|
|
if v == nil {
|
|
|
|
// Bucket
|
|
|
|
if uB, err = b.CreateBucketIfNotExists([]byte(k)); err == nil {
|
|
|
|
if uSB, err = uB.CreateBucketIfNotExists([]byte("stats")); err == nil {
|
2015-10-30 14:33:18 +00:00
|
|
|
var st int
|
|
|
|
var key string
|
2015-10-30 13:10:05 +00:00
|
|
|
uSB.ForEach(func(k, v []byte) error {
|
2015-10-30 14:33:18 +00:00
|
|
|
key = string(k)
|
|
|
|
if st, err = bktGetInt(uSB, key); err == nil {
|
|
|
|
ret[key] = ret[key] + st
|
2015-10-30 13:10:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-30 14:33:18 +00:00
|
|
|
return err
|
2015-10-30 13:10:05 +00:00
|
|
|
})
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return ret, err
|
|
|
|
}
|
|
|
|
|
2015-11-05 12:55:49 +00:00
|
|
|
func getUserList() []string {
|
|
|
|
openDatabase()
|
|
|
|
// First build channel list
|
|
|
|
var users []string
|
|
|
|
db.Update(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket([]byte("users"))
|
|
|
|
return b.ForEach(func(k, v []byte) error {
|
|
|
|
users = append(users, string(k))
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return users
|
|
|
|
}
|
|
|
|
|
|
|
|
func getUserName(uid string) string {
|
|
|
|
var ret string
|
|
|
|
openDatabase()
|
|
|
|
db.View(func(tx *bolt.Tx) error {
|
|
|
|
var b, uB, uIB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
|
|
|
|
b = tx.Bucket([]byte("users"))
|
|
|
|
if b == nil {
|
|
|
|
return fmt.Errorf("Error opening 'users' bucket")
|
|
|
|
}
|
|
|
|
if uB = b.Bucket([]byte(uid)); uB == nil {
|
|
|
|
return fmt.Errorf("Error opening user bucket (%s)", uid)
|
|
|
|
}
|
|
|
|
if uIB = uB.Bucket([]byte("info")); uIB == nil {
|
|
|
|
return fmt.Errorf("Error opening user info bucket (%s/info)", uid)
|
|
|
|
}
|
|
|
|
ret, err = bktGetString(uIB, "name")
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2015-11-02 19:48:29 +00:00
|
|
|
func getChannelMessageCount(channel string) int {
|
|
|
|
var ret int
|
|
|
|
openDatabase()
|
|
|
|
db.View(func(tx *bolt.Tx) error {
|
|
|
|
var b, chB, chMB *bolt.Bucket
|
|
|
|
var err error
|
|
|
|
|
|
|
|
b = tx.Bucket([]byte("channels"))
|
|
|
|
if chB = b.Bucket([]byte(channel)); chB != nil {
|
|
|
|
if chMB = chB.Bucket([]byte("messages")); chMB != nil {
|
|
|
|
ret = chMB.Stats().BucketN
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func getChannelList() []string {
|
|
|
|
openDatabase()
|
|
|
|
// First build channel list
|
|
|
|
var chans []string
|
|
|
|
db.Update(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket([]byte("channels"))
|
|
|
|
return b.ForEach(func(k, v []byte) error {
|
|
|
|
chans = append(chans, string(k))
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
})
|
|
|
|
closeDatabase()
|
|
|
|
return chans
|
|
|
|
}
|
|
|
|
|
|
|
|
func getTotalChannelMsgCount() int {
|
|
|
|
var ret int
|
|
|
|
openDatabase()
|
|
|
|
// First build channel list
|
|
|
|
chans := getChannelList()
|
|
|
|
for _, k := range chans {
|
|
|
|
ret = ret + getChannelMessageCount(k)
|
|
|
|
}
|
|
|
|
closeDatabase()
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
func bktGetBucket(b *bolt.Bucket, key string) (*bolt.Bucket, error) {
|
|
|
|
bkt := b.Bucket([]byte(key))
|
|
|
|
if bkt != nil {
|
|
|
|
return nil, fmt.Errorf("Bucket (%s) not found.", key)
|
|
|
|
}
|
|
|
|
return bkt, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func bktGetBytes(b *bolt.Bucket, key string) ([]byte, error) {
|
|
|
|
bb := b.Get([]byte(key))
|
|
|
|
if bb == nil {
|
|
|
|
return nil, fmt.Errorf("Error reading key (%s)", key)
|
|
|
|
}
|
|
|
|
return bb, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func bktGetString(b *bolt.Bucket, key string) (string, error) {
|
|
|
|
bb, err := bktGetBytes(b, key)
|
|
|
|
return string(bb), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func bktPutString(b *bolt.Bucket, key string, val string) error {
|
|
|
|
return b.Put([]byte(key), []byte(val))
|
|
|
|
}
|
|
|
|
|
|
|
|
func bktGetBool(b *bolt.Bucket, key string) (bool, error) {
|
|
|
|
bb, err := bktGetBytes(b, key)
|
|
|
|
return (string(bb) == "true"), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func bktPutBool(b *bolt.Bucket, key string, val bool) error {
|
|
|
|
if val {
|
|
|
|
return b.Put([]byte(key), []byte("true"))
|
|
|
|
}
|
|
|
|
return b.Put([]byte(key), []byte("false"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func bktGetInt(b *bolt.Bucket, key string) (int, error) {
|
|
|
|
bb, err := bktGetBytes(b, key)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return strconv.Atoi(string(bb))
|
|
|
|
}
|
|
|
|
|
|
|
|
func bktPutInt(b *bolt.Bucket, key string, val int) error {
|
|
|
|
return b.Put([]byte(key), []byte(fmt.Sprintf("%d", val)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func bktGetTime(b *bolt.Bucket, key string) (time.Time, error) {
|
|
|
|
bs, err := bktGetString(b, key)
|
|
|
|
if err != nil {
|
|
|
|
return time.Time{}, err
|
|
|
|
}
|
|
|
|
return time.Parse(time.RFC3339, bs)
|
|
|
|
}
|
|
|
|
|
|
|
|
func bktPutTime(b *bolt.Bucket, key string, val time.Time) error {
|
|
|
|
bs := val.Format(time.RFC3339)
|
|
|
|
return b.Put([]byte(key), []byte(bs))
|
|
|
|
}
|
|
|
|
|
2015-10-29 19:16:45 +00:00
|
|
|
func convertSlackTimestamp(ts string) time.Time {
|
|
|
|
var ret time.Time
|
|
|
|
txtArr := strings.Split(ts, ".")
|
|
|
|
if t, err := strconv.Atoi(txtArr[0]); err == nil {
|
|
|
|
rawts := int64(t)
|
|
|
|
ret = time.Unix(0, rawts*1000000000)
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
// All user stats are saved in the db like so:
|
|
|
|
// users (bucket)
|
|
|
|
// |- username1 (bucket)
|
|
|
|
// | |-info (bucket)
|
|
|
|
// | | |-userinfo1 (pair)
|
|
|
|
// | | \-userinfo2 (pair)
|
|
|
|
// | |
|
|
|
|
// | |-messages (bucket)
|
|
|
|
// | | |-message1 (message bucket)
|
|
|
|
// | | \-message2 (message bucket)
|
|
|
|
// | |
|
|
|
|
// | \-stats (bucket)
|
|
|
|
// | |-stat1 (pair)
|
|
|
|
// | \-stat2 (pair)
|
|
|
|
// |
|
|
|
|
// \- username2 (bucket)
|
|
|
|
// |-info (bucket)
|
|
|
|
// | |-userinfo1 (pair)
|
|
|
|
// | \-userinfo2 (pair)
|
|
|
|
// |
|
|
|
|
// |-messages (bucket)
|
|
|
|
// | |-message1 (message bucket)
|
|
|
|
// | \-message2 (message bucket)
|
|
|
|
// |
|
|
|
|
// \-stats (bucket)
|
|
|
|
// |-stat1 (pair)
|
|
|
|
// \-stat2 (pair)
|
|
|
|
//
|
|
|
|
// And channel stats are saved in the db like so:
|
|
|
|
// channels (bucket)
|
|
|
|
// |- channel1 (bucket)
|
|
|
|
// | |-info (bucket)
|
|
|
|
// | | |-chaninfo1 (pair)
|
|
|
|
// | | \-chaninfo2 (pair)
|
|
|
|
// | |
|
|
|
|
// | |-messages (bucket)
|
|
|
|
// | | |-message1 (message bucket)
|
|
|
|
// | | \-message2 (message bucket)
|
|
|
|
// | |
|
|
|
|
// | \-stats (bucket)
|
|
|
|
// | |-stat1 (pair)
|
|
|
|
// | \-stat2 (pair)
|
|
|
|
// |
|
|
|
|
// \- channel2 (bucket)
|
|
|
|
// |-info (bucket)
|
|
|
|
// | |-chaninfo1 (pair)
|
|
|
|
// | \-chaninfo2 (pair)
|
|
|
|
// |
|
|
|
|
// |-messages (bucket)
|
|
|
|
// | |-message1 (message bucket)
|
|
|
|
// | \-message2 (message bucket)
|
|
|
|
// |
|
|
|
|
// \-stats (bucket)
|
|
|
|
// |-stat1 (pair)
|
|
|
|
// \-stat2 (pair)
|
|
|
|
//
|
|
|
|
// A (message bucket) looks like this:
|
|
|
|
// messageid (unique to parent bucket)
|
|
|
|
// |-Type (slack event type)
|
|
|
|
// |-User (slack user ID)
|
|
|
|
// |-Text (Text of the message)
|
|
|
|
// \-Time RFC3339 format timestamp
|