Functioning with leaderboard updates
This commit is contained in:
parent
8a380f4377
commit
8e468c4ce9
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
slack-api-key
|
||||||
|
cookies
|
||||||
|
aoc_leaderboard.json
|
||||||
|
aocbot
|
||||||
|
aocbot.db
|
||||||
|
runbot.sh
|
||||||
|
*.log
|
18
aoc_structs.go
Normal file
18
aoc_structs.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Leaderboard struct {
|
||||||
|
OwnerID string `json:"owner_id"`
|
||||||
|
Event string `json:"event"`
|
||||||
|
Members map[string]Member `json:"members"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Member struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Stars int `json:"stars"`
|
||||||
|
RawStarTs string `json:"last_star_ts"`
|
||||||
|
LastStarTs time.Time
|
||||||
|
Name string `json:"name"`
|
||||||
|
SlackID string
|
||||||
|
}
|
247
aocbot.go
247
aocbot.go
@ -3,7 +3,12 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gogs.bullercodeworks.com/brian/boltease"
|
"gogs.bullercodeworks.com/brian/boltease"
|
||||||
@ -11,42 +16,14 @@ import (
|
|||||||
|
|
||||||
const programName = "aocbot"
|
const programName = "aocbot"
|
||||||
|
|
||||||
// Message Processors are interfaces for interacting.
|
|
||||||
type messageProcessor interface {
|
|
||||||
GetName() string
|
|
||||||
GetHelp() string
|
|
||||||
ProcessMessage(s *Slack, m *Message)
|
|
||||||
ProcessAdminMessage(s *Slack, m *Message)
|
|
||||||
ProcessBotMessage(s *Slack, m *Message)
|
|
||||||
ProcessUserMessage(s *Slack, m *Message)
|
|
||||||
ProcessAdminUserMessage(s *Slack, m *Message)
|
|
||||||
ProcessBotUserMessage(s *Slack, m *Message)
|
|
||||||
ProcessChannelMessage(s *Slack, m *Message)
|
|
||||||
ProcessAdminChannelMessage(s *Slack, m *Message)
|
|
||||||
ProcessBotChannelMessage(s *Slack, m *Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageProcessors []messageProcessor
|
|
||||||
|
|
||||||
// Message Processors are interfaces for accumulating statistics.
|
|
||||||
type statProcessor interface {
|
|
||||||
GetName() string
|
|
||||||
GetStatKeys() []string
|
|
||||||
Initialize()
|
|
||||||
ProcessMessage(m *Message)
|
|
||||||
ProcessBotMessage(m *Message)
|
|
||||||
ProcessUserMessage(m *Message)
|
|
||||||
ProcessBotUserMessage(m *Message)
|
|
||||||
ProcessChannelMessage(m *Message)
|
|
||||||
ProcessBotChannelMessage(m *Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
var statProcessors []statProcessor
|
|
||||||
var db *boltease.DB
|
var db *boltease.DB
|
||||||
|
var boardID string
|
||||||
|
|
||||||
|
var slackChannel string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) != 2 {
|
if len(os.Args) != 3 {
|
||||||
fmt.Fprintf(os.Stderr, "usage: "+programName+" <slack-bot-token>\n")
|
fmt.Fprintf(os.Stderr, "usage: "+programName+" <slack-bot-token> <aoc-leaderboard-number>\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,22 +33,68 @@ func main() {
|
|||||||
if db, err = getDatabase(); err != nil {
|
if db, err = getDatabase(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
slackChannel = "C0G3X1M5K"
|
||||||
|
|
||||||
|
// DevICT Leaderboard: 3549
|
||||||
|
boardID = os.Args[2]
|
||||||
|
|
||||||
if slack, err = CreateSlack(os.Args[1]); err != nil {
|
if slack, err = CreateSlack(os.Args[1]); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
statBotMain(slack)
|
aocBotMain(slack)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the main function for the statbot
|
// This is the main function for the statbot
|
||||||
func statBotMain(slack *Slack) {
|
func aocBotMain(slack *Slack) {
|
||||||
// start a websocket-based Real Time API session
|
// start a websocket-based Real Time API session
|
||||||
registerMessageProcessor(new(generalProcessor))
|
|
||||||
|
|
||||||
fmt.Println("aoc-bot ready, ^C exits")
|
fmt.Println("aoc-bot ready, ^C exits")
|
||||||
|
|
||||||
writeToLog("== " + time.Now().Format(time.RFC3339) + " - Bot Started ==\n")
|
writeToLog("== " + time.Now().Format(time.RFC3339) + " - Bot Started ==\n")
|
||||||
|
var lastAoCUpdate time.Time
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
var err error
|
||||||
|
lastAoCUpdate = time.Now()
|
||||||
|
var leaderboard *Leaderboard
|
||||||
|
fmt.Println(lastAoCUpdate.Format(time.RFC3339) + ": Fetching Online Leaderboard")
|
||||||
|
if leaderboard, err = getAoCLeaderboard(boardID); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range leaderboard.Members {
|
||||||
|
var mbr *Member
|
||||||
|
if mbr, err = getAoCUser(v.ID); err != nil {
|
||||||
|
// Member doesn't exist in db, add it
|
||||||
|
// Notify br0xen ( U030RD9NU )
|
||||||
|
m := new(Message)
|
||||||
|
m.Type = "message"
|
||||||
|
m.Channel = "D0D793N5R"
|
||||||
|
m.Text = "AoC Leaderboard has a new member! " + v.Name
|
||||||
|
fmt.Println("New Leaderboard Member Found: " + v.Name)
|
||||||
|
if err = slack.postMessage(*m); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
saveAoCUser(&v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if mbr.Stars != v.Stars {
|
||||||
|
// Number of stars has changed
|
||||||
|
fmt.Println(v.ID + "(" + v.Name + "): " + v.LastStarTs.Format(time.RFC3339))
|
||||||
|
m := new(Message)
|
||||||
|
m.Type = "message"
|
||||||
|
m.Channel = slackChannel
|
||||||
|
m.Text = ":christmas_tree: " + v.Name + " now has " + strconv.Itoa(v.Stars) + " stars! :christmas_tree:"
|
||||||
|
if err = slack.postMessage(*m); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
saveAoCUser(&v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(time.Minute * 10)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// read each incoming message
|
// read each incoming message
|
||||||
m, err := slack.getMessage()
|
m, err := slack.getMessage()
|
||||||
@ -79,6 +102,7 @@ func statBotMain(slack *Slack) {
|
|||||||
writeToLog(" " + time.Now().Format(time.RFC3339) + " - Received Message\n")
|
writeToLog(" " + time.Now().Format(time.RFC3339) + " - Received Message\n")
|
||||||
processMessage(slack, &m)
|
processMessage(slack, &m)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
writeToLog("== " + time.Now().Format(time.RFC3339) + " - Bot Stopped ==\n\n")
|
writeToLog("== " + time.Now().Format(time.RFC3339) + " - Bot Stopped ==\n\n")
|
||||||
}
|
}
|
||||||
@ -94,33 +118,15 @@ func processMessage(slack *Slack, m *Message) {
|
|||||||
var usr *User
|
var usr *User
|
||||||
|
|
||||||
// Check if we know who the user is
|
// Check if we know who the user is
|
||||||
usr, err = getUserInfo(m.User)
|
usr, err = getUser(m.User)
|
||||||
// If the user information hasn't been updated in the last day, update it.
|
// If the user information hasn't been updated in the last day, update it.
|
||||||
if err != nil || usr.LastUpdated.IsZero() || time.Since(usr.LastUpdated) > (time.Hour*24) {
|
if err != nil || usr.LastUpdated.IsZero() || time.Since(usr.LastUpdated) > (time.Hour*24) {
|
||||||
if u, ue := slack.getUserInfo(m.User); ue == nil {
|
if u, ue := slack.getUserInfo(m.User); ue == nil {
|
||||||
saveUserInfo(u)
|
saveUser(u)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, stats := range statProcessors {
|
|
||||||
if usr.IsBot {
|
|
||||||
stats.ProcessBotMessage(m)
|
|
||||||
} else {
|
|
||||||
stats.ProcessMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, proc := range messageProcessors {
|
|
||||||
if isAdmin(m.User) {
|
|
||||||
proc.ProcessAdminMessage(slack, m)
|
|
||||||
}
|
|
||||||
if usr.IsBot {
|
|
||||||
proc.ProcessBotMessage(slack, m)
|
|
||||||
} else {
|
|
||||||
proc.ProcessMessage(slack, m)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Process the message
|
||||||
if m.Channel != "" {
|
if m.Channel != "" {
|
||||||
// Check if we know what the channel is
|
// Check if we know what the channel is
|
||||||
chnl, err := getChannelInfo(m.Channel)
|
chnl, err := getChannelInfo(m.Channel)
|
||||||
@ -137,30 +143,133 @@ func processMessage(slack *Slack, m *Message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isDirectMessage {
|
if isDirectMessage {
|
||||||
for _, proc := range messageProcessors {
|
if m.User == "U030RD9NU" {
|
||||||
if isAdmin(m.User) {
|
// message from br0xen
|
||||||
proc.ProcessAdminUserMessage(slack, m)
|
if strings.HasPrefix(m.Text, "!aoc") {
|
||||||
|
var slackId, aocNm, op string
|
||||||
|
flds := strings.Fields(m.Text)
|
||||||
|
if len(flds) < 2 {
|
||||||
|
// No operation specified
|
||||||
|
return
|
||||||
}
|
}
|
||||||
proc.ProcessUserMessage(slack, m)
|
op = flds[1]
|
||||||
|
if op == "help" {
|
||||||
|
// TODO: Send help message
|
||||||
|
}
|
||||||
|
if op == "match" {
|
||||||
|
re := regexp.MustCompile("[<\"]([^>]*)[>\"]")
|
||||||
|
matchUp := re.FindAllString(m.Text, -1)
|
||||||
|
|
||||||
|
for i := range matchUp {
|
||||||
|
if matchUp[i][0] == '<' {
|
||||||
|
slackId = matchUp[i][1 : len(matchUp[i])-1]
|
||||||
|
}
|
||||||
|
if matchUp[i][0] == '"' {
|
||||||
|
aocNm = matchUp[i][1 : len(matchUp[i])-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var m *Member
|
||||||
|
if m, err = getAoCUserByName(aocNm); err != nil {
|
||||||
|
// AoC Member doesn't exist in DB
|
||||||
|
fmt.Println("AoC User " + aocNm + " doesn't exist in DB")
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.SlackID = slackId
|
||||||
|
saveAoCUser(m)
|
||||||
|
} else if op == "ping" {
|
||||||
|
m := new(Message)
|
||||||
|
m.Type = "message"
|
||||||
|
m.Channel = "D0D793N5R"
|
||||||
|
m.Text = ":christmas_tree: PONG :christmas_tree:"
|
||||||
|
if err = slack.postMessage(*m); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
ID: 0
|
||||||
|
Type: message
|
||||||
|
Channel: D0D793N5R
|
||||||
|
User: U030RD9NU
|
||||||
|
Name:
|
||||||
|
Text: testing
|
||||||
|
Ts: 1481044675.000003
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
fmt.Println("--- Channel Message Received")
|
||||||
|
fmt.Println(m.Channel, m.User, m.Text)
|
||||||
|
if m.Channel == slackChannel {
|
||||||
|
// In the AoC channel
|
||||||
|
if strings.HasPrefix(m.Text, "!aoc") {
|
||||||
|
var op string
|
||||||
|
flds := strings.Fields(m.Text)
|
||||||
|
if len(flds) < 2 {
|
||||||
|
// No operation specified
|
||||||
|
return
|
||||||
|
}
|
||||||
|
op = flds[1]
|
||||||
|
if op == "ping" {
|
||||||
|
m := new(Message)
|
||||||
|
m.Type = "message"
|
||||||
|
m.Channel = slackChannel
|
||||||
|
m.Text = ":christmas_tree: PONG :christmas_tree:"
|
||||||
|
if err = slack.postMessage(*m); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Process Channel Message
|
||||||
|
/*
|
||||||
for _, proc := range messageProcessors {
|
for _, proc := range messageProcessors {
|
||||||
proc.ProcessChannelMessage(slack, m)
|
proc.ProcessChannelMessage(slack, m)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerMessageProcessor(b messageProcessor) {
|
func getAoCLeaderboard(boardId string) (*Leaderboard, error) {
|
||||||
// Register a Message Processor
|
var err error
|
||||||
// Make sure that we haven't already registered it
|
var req *http.Request
|
||||||
for _, proc := range messageProcessors {
|
var content []byte
|
||||||
if proc.GetName() == b.GetName() {
|
var resp *http.Response
|
||||||
panic(fmt.Errorf("Attempted to Re-register Message Processor %s", b.GetName()))
|
var body []byte
|
||||||
|
leaderboard := new(Leaderboard)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
boardString := "http://adventofcode.com/2016/leaderboard/private/view/" + boardId + ".json"
|
||||||
|
|
||||||
|
req, err = http.NewRequest("GET", boardString, nil)
|
||||||
|
// Read in cookies
|
||||||
|
content, err = ioutil.ReadFile("./cookies")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error reading Cookies")
|
||||||
|
return leaderboard, err
|
||||||
}
|
}
|
||||||
|
line := strings.TrimSpace(string(content))
|
||||||
|
req.Header.Add("Cookie", line)
|
||||||
|
|
||||||
|
resp, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error getting leaderboard")
|
||||||
}
|
}
|
||||||
messageProcessors = append(messageProcessors, b)
|
defer resp.Body.Close()
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error reading response body.")
|
||||||
|
return leaderboard, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(body, &leaderboard)
|
||||||
|
for k, mbr := range leaderboard.Members {
|
||||||
|
mbr.LastStarTs, err = time.Parse("2006-01-02T15:04:05-0700", mbr.RawStarTs)
|
||||||
|
leaderboard.Members[k] = mbr
|
||||||
|
}
|
||||||
|
return leaderboard, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeToLog(d string) {
|
func writeToLog(d string) {
|
||||||
|
196
model.go
196
model.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gogs.bullercodeworks.com/brian/boltease"
|
"gogs.bullercodeworks.com/brian/boltease"
|
||||||
@ -9,44 +10,46 @@ import (
|
|||||||
func getDatabase() (*boltease.DB, error) {
|
func getDatabase() (*boltease.DB, error) {
|
||||||
db, err := boltease.Create("aocbot.db", 0644, nil)
|
db, err := boltease.Create("aocbot.db", 0644, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return db, err
|
||||||
}
|
}
|
||||||
// Make sure that we have a 'users' bucket
|
// Make sure that we have a 'users' bucket
|
||||||
db.MkBucketPath([]string{"users"})
|
db.MkBucketPath([]string{"users"})
|
||||||
// Make sure that we have a 'channels' bucket
|
// Make sure that we have a 'channels' bucket
|
||||||
db.MkBucketPath([]string{"channels"})
|
db.MkBucketPath([]string{"channels"})
|
||||||
|
// Make sure that we have a 'aocmembers' bucket
|
||||||
|
db.MkBucketPath([]string{"AoCMembers"})
|
||||||
|
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *boltease.DB) saveUser(usr *User) error {
|
func saveUser(usr *User) error {
|
||||||
var err error
|
var err error
|
||||||
if err = db.OpenDB(); err != nil {
|
if err = db.OpenDB(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer db.CloseDB()
|
defer db.CloseDB()
|
||||||
|
|
||||||
usr := []string{"users", usr.ID}
|
usrPath := []string{"users", usr.ID}
|
||||||
db.SetValue(append(usr, "Name"), usr.Name)
|
db.SetValue(usrPath, "Name", usr.Name)
|
||||||
db.SetBool(append(usr, "Deleted"), usr.Deleted)
|
db.SetBool(usrPath, "Deleted", usr.Deleted)
|
||||||
db.SetValue(append(usr, "Status"), usr.Status)
|
db.SetValue(usrPath, "Status", usr.Status)
|
||||||
db.SetValue(append(usr, "Color"), usr.Color)
|
db.SetValue(usrPath, "Color", usr.Color)
|
||||||
db.SetValue(append(usr, "RealName"), usr.RealName)
|
db.SetValue(usrPath, "RealName", usr.RealName)
|
||||||
db.SetValue(append(usr, "TZ"), usr.TZ)
|
db.SetValue(usrPath, "TZ", usr.TZ)
|
||||||
db.SetValue(append(usr, "TZLabel"), usr.TZLabel)
|
db.SetValue(usrPath, "TZLabel", usr.TZLabel)
|
||||||
db.SetInt(append(usr, "TZOffset"), usr.TZOffset)
|
db.SetInt(usrPath, "TZOffset", usr.TZOffset)
|
||||||
db.SetBool(append(usr, "IsAdmin"), usr.IsAdmin)
|
db.SetBool(usrPath, "IsAdmin", usr.IsAdmin)
|
||||||
db.SetBool(append(usr, "IsOwner"), usr.IsOwner)
|
db.SetBool(usrPath, "IsOwner", usr.IsOwner)
|
||||||
db.SetBool(append(usr, "IsPrimaryOwner"), usr.IsPrimaryOwner)
|
db.SetBool(usrPath, "IsPrimaryOwner", usr.IsPrimaryOwner)
|
||||||
db.SetBool(append(usr, "IsRestricted"), usr.IsRestricted)
|
db.SetBool(usrPath, "IsRestricted", usr.IsRestricted)
|
||||||
db.SetBool(append(usr, "IsUltraRestricted"), usr.IsUltraRestricted)
|
db.SetBool(usrPath, "IsUltraRestricted", usr.IsUltraRestricted)
|
||||||
db.SetBool(append(usr, "IsBot"), usr.IsBot)
|
db.SetBool(usrPath, "IsBot", usr.IsBot)
|
||||||
db.SetBool(append(usr, "HasFiles"), usr.HasFiles)
|
db.SetBool(usrPath, "HasFiles", usr.HasFiles)
|
||||||
db.SetTimestamp(append(usr, "LastUpdated"), time.Now())
|
db.SetTimestamp(usrPath, "LastUpdated", time.Now())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *boltease.DB) getUser(usrId string) (*User, error) {
|
func getUser(usrId string) (*User, error) {
|
||||||
usr := new(User)
|
usr := new(User)
|
||||||
var err error
|
var err error
|
||||||
if err = db.OpenDB(); err != nil {
|
if err = db.OpenDB(); err != nil {
|
||||||
@ -54,53 +57,172 @@ func (db *boltease.DB) getUser(usrId string) (*User, error) {
|
|||||||
}
|
}
|
||||||
defer db.CloseDB()
|
defer db.CloseDB()
|
||||||
|
|
||||||
upath := []string{"users", usrID}
|
upath := []string{"users", usrId}
|
||||||
if usr.Name, err = db.GetValue(append(upath, "Name")); err != nil {
|
if usr.Name, err = db.GetValue(upath, "Name"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.Deleted, err = db.GetBool(append(upath, "Deleted")); err != nil {
|
if usr.Deleted, err = db.GetBool(upath, "Deleted"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.Status, err = db.GetValue(append(upath, "Status")); err != nil {
|
if usr.Status, err = db.GetValue(upath, "Status"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.Color, err = db.GetValue(append(upath, "Color")); err != nil {
|
if usr.Color, err = db.GetValue(upath, "Color"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.RealName, err = db.GetValue(append(upath, "RealName")); err != nil {
|
if usr.RealName, err = db.GetValue(upath, "RealName"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.TZ, err = db.GetValue(append(upath, "TZ")); err != nil {
|
if usr.TZ, err = db.GetValue(upath, "TZ"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.TZLabel, err = db.GetValue(append(upath, "TZLabel")); err != nil {
|
if usr.TZLabel, err = db.GetValue(upath, "TZLabel"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.TZOffset, err = db.GetInt(append(upath, "TZOffset")); err != nil {
|
if usr.TZOffset, err = db.GetInt(upath, "TZOffset"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.IsAdmin, err = db.GetBool(append(upath, "IsAdmin")); err != nil {
|
if usr.IsAdmin, err = db.GetBool(upath, "IsAdmin"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.IsOwner, err = db.GetBool(append(upath, "IsOwner")); err != nil {
|
if usr.IsOwner, err = db.GetBool(upath, "IsOwner"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.IsPrimaryOwner, err = db.GetBool(append(upath, "IsPrimaryOwner")); err != nil {
|
if usr.IsPrimaryOwner, err = db.GetBool(upath, "IsPrimaryOwner"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.IsRestricted, err = db.GetBool(append(upath, "IsRestricted")); err != nil {
|
if usr.IsRestricted, err = db.GetBool(upath, "IsRestricted"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.IsUltraRestricted, err = db.GetBool(append(upath, "IsUltraRestricted")); err != nil {
|
if usr.IsUltraRestricted, err = db.GetBool(upath, "IsUltraRestricted"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.IsBot, err = db.GetBool(append(upath, "IsBot")); err != nil {
|
if usr.IsBot, err = db.GetBool(upath, "IsBot"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
if usr.HasFiles, err = db.GetBool(append(upath, "HasFiles")); err != nil {
|
if usr.HasFiles, err = db.GetBool(upath, "HasFiles"); err != nil {
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
usr.LastUpdated, err = db.GetTimestamp(append(upath, "LastUpdated"))
|
usr.LastUpdated, err = db.GetTimestamp(upath, "LastUpdated")
|
||||||
|
|
||||||
return usr, err
|
return usr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getChannelInfo(chanId string) (*Channel, error) {
|
||||||
|
ret := new(Channel)
|
||||||
|
var err error
|
||||||
|
if err = db.OpenDB(); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
defer db.CloseDB()
|
||||||
|
|
||||||
|
cPath := []string{"channels", chanId}
|
||||||
|
if ret.ID, err = db.GetValue(cPath, "ID"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
if ret.Name, err = db.GetValue(cPath, "Name"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
if ret.IsChannel, err = db.GetBool(cPath, "IsChannel"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
if ret.IsGeneral, err = db.GetBool(cPath, "IsGeneral"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
if ret.IsMember, err = db.GetBool(cPath, "IsMember"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
var mbrString string
|
||||||
|
if mbrString, err = db.GetValue(cPath, "Members"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
ret.Members = strings.Split(mbrString, ",")
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveChannelInfo(c *Channel) error {
|
||||||
|
var err error
|
||||||
|
if err = db.OpenDB(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer db.CloseDB()
|
||||||
|
|
||||||
|
cPath := []string{"channels", c.ID}
|
||||||
|
db.SetValue(cPath, "ID", c.ID)
|
||||||
|
db.SetValue(cPath, "Name", c.Name)
|
||||||
|
db.SetBool(cPath, "IsChannel", c.IsChannel)
|
||||||
|
db.SetBool(cPath, "IsGeneral", c.IsGeneral)
|
||||||
|
db.SetBool(cPath, "IsMember", c.IsMember)
|
||||||
|
db.SetValue(cPath, "Members", strings.Join(c.Members, ","))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveAoCUser(m *Member) error {
|
||||||
|
var err error
|
||||||
|
if err = db.OpenDB(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer db.CloseDB()
|
||||||
|
|
||||||
|
mPath := []string{"AoCMembers", m.ID}
|
||||||
|
db.SetValue(mPath, "ID", m.ID)
|
||||||
|
db.SetValue(mPath, "Name", m.Name)
|
||||||
|
db.SetInt(mPath, "Stars", m.Stars)
|
||||||
|
db.SetTimestamp(mPath, "LastStarTs", m.LastStarTs)
|
||||||
|
db.SetValue(mPath, "SlackID", m.SlackID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAoCUser(mbrId string) (*Member, error) {
|
||||||
|
ret := new(Member)
|
||||||
|
var err error
|
||||||
|
if err = db.OpenDB(); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
defer db.CloseDB()
|
||||||
|
|
||||||
|
mPath := []string{"AoCMembers", mbrId}
|
||||||
|
if ret.ID, err = db.GetValue(mPath, "ID"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
if ret.Stars, err = db.GetInt(mPath, "Stars"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
if ret.LastStarTs, err = db.GetTimestamp(mPath, "LastStarTs"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
if ret.Name, err = db.GetValue(mPath, "Name"); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
ret.SlackID, err = db.GetValue(mPath, "SlackID")
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAoCUserByName(nm string) (*Member, error) {
|
||||||
|
var err error
|
||||||
|
if err = db.OpenDB(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer db.CloseDB()
|
||||||
|
|
||||||
|
mPath := []string{"AoCMembers"}
|
||||||
|
var bktList []string
|
||||||
|
if bktList, err = db.GetBucketList(mPath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var dbNm, dbId string
|
||||||
|
for i := range bktList {
|
||||||
|
dbNm, err = db.GetValue(append(mPath, bktList[i]), "Name")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if nm == dbNm {
|
||||||
|
dbId, err = db.GetValue(append(mPath, bktList[i]), "ID")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return getAoCUser(dbId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
91
slack.go
91
slack.go
@ -5,6 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -91,7 +93,15 @@ func (s *Slack) getMessage() (Message, error) {
|
|||||||
|
|
||||||
func (s *Slack) sendMessageToUser(uid string, msg string) error {
|
func (s *Slack) sendMessageToUser(uid string, msg string) error {
|
||||||
// TODO: Actually send a direct message to uid
|
// TODO: Actually send a direct message to uid
|
||||||
return nil
|
m := new(Message)
|
||||||
|
m.ID = atomic.AddUint64(&counter, 1)
|
||||||
|
m.Type = "message"
|
||||||
|
m.Channel = ""
|
||||||
|
m.User = uid
|
||||||
|
m.Name = ""
|
||||||
|
m.Text = msg
|
||||||
|
|
||||||
|
return websocket.JSON.Send(s.socket, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) getUserInfo(uid string) (*User, error) {
|
func (s *Slack) getUserInfo(uid string) (*User, error) {
|
||||||
@ -122,6 +132,41 @@ func (s *Slack) getUserInfo(uid string) (*User, error) {
|
|||||||
return respObj.User, nil
|
return respObj.User, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Slack) getChannelList() ([]Channel, error) {
|
||||||
|
url := fmt.Sprintf("https://slack.com/api/channels.list?token=%s", s.apiToken)
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
err = fmt.Errorf("API request failed with code %d", resp.StatusCode)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Println(string(body))
|
||||||
|
|
||||||
|
var respObj responseChannelList
|
||||||
|
err = json.Unmarshal(body, &respObj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !respObj.Ok {
|
||||||
|
err = fmt.Errorf("Slack error: %s", respObj.Error)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range respObj.Channels {
|
||||||
|
respObj.Channels[i].LastRead = convertSlackTimestamp(respObj.Channels[i].LastReadRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return respObj.Channels, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Slack) getChannelInfo(cid string) (*Channel, error) {
|
func (s *Slack) getChannelInfo(cid string) (*Channel, error) {
|
||||||
url := fmt.Sprintf("https://slack.com/api/channels.info?token=%s&channel=%s", s.apiToken, cid)
|
url := fmt.Sprintf("https://slack.com/api/channels.info?token=%s&channel=%s", s.apiToken, cid)
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
@ -153,34 +198,6 @@ func (s *Slack) getChannelInfo(cid string) (*Channel, error) {
|
|||||||
return respObj.Channel, nil
|
return respObj.Channel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) joinChannel(c *Channel) error {
|
|
||||||
url := fmt.Sprintf("https://slack.com/api/channels.join?token=%s&name=%s", s.apiToken, c.Name)
|
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
err = fmt.Errorf("API request failed with code %d", resp.StatusCode)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var respObj responseChannelLookup
|
|
||||||
err = json.Unmarshal(body, &respObj)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !respObj.Ok {
|
|
||||||
err = fmt.Errorf("Slack error: %s", respObj.Error)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// These structures represent the response of the Slack API events
|
// These structures represent the response of the Slack API events
|
||||||
// Only some fields are included. The rest are ignored by json.Unmarshal.
|
// Only some fields are included. The rest are ignored by json.Unmarshal.
|
||||||
type responseRtmStart struct {
|
type responseRtmStart struct {
|
||||||
@ -205,3 +222,19 @@ type responseChannelLookup struct {
|
|||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Channel *Channel `json:"channel"`
|
Channel *Channel `json:"channel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type responseChannelList struct {
|
||||||
|
Ok bool `json:"ok"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Channels []Channel `json:"channels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
@ -39,6 +39,15 @@ type Channel struct {
|
|||||||
LastUpdated time.Time
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// User object
|
// User object
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
Reference in New Issue
Block a user