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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gogs.bullercodeworks.com/brian/boltease"
|
||||
@ -11,42 +16,14 @@ import (
|
||||
|
||||
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 boardID string
|
||||
|
||||
var slackChannel string
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Fprintf(os.Stderr, "usage: "+programName+" <slack-bot-token>\n")
|
||||
if len(os.Args) != 3 {
|
||||
fmt.Fprintf(os.Stderr, "usage: "+programName+" <slack-bot-token> <aoc-leaderboard-number>\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@ -56,22 +33,68 @@ func main() {
|
||||
if db, err = getDatabase(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
slackChannel = "C0G3X1M5K"
|
||||
|
||||
// DevICT Leaderboard: 3549
|
||||
boardID = os.Args[2]
|
||||
|
||||
if slack, err = CreateSlack(os.Args[1]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
statBotMain(slack)
|
||||
aocBotMain(slack)
|
||||
}
|
||||
|
||||
// This is the main function for the statbot
|
||||
func statBotMain(slack *Slack) {
|
||||
func aocBotMain(slack *Slack) {
|
||||
// start a websocket-based Real Time API session
|
||||
registerMessageProcessor(new(generalProcessor))
|
||||
|
||||
fmt.Println("aoc-bot ready, ^C exits")
|
||||
|
||||
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 {
|
||||
// read each incoming message
|
||||
m, err := slack.getMessage()
|
||||
@ -79,6 +102,7 @@ func statBotMain(slack *Slack) {
|
||||
writeToLog(" " + time.Now().Format(time.RFC3339) + " - Received Message\n")
|
||||
processMessage(slack, &m)
|
||||
}
|
||||
|
||||
}
|
||||
writeToLog("== " + time.Now().Format(time.RFC3339) + " - Bot Stopped ==\n\n")
|
||||
}
|
||||
@ -94,33 +118,15 @@ func processMessage(slack *Slack, m *Message) {
|
||||
var usr *User
|
||||
|
||||
// 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 err != nil || usr.LastUpdated.IsZero() || time.Since(usr.LastUpdated) > (time.Hour*24) {
|
||||
if u, ue := slack.getUserInfo(m.User); ue == nil {
|
||||
saveUserInfo(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)
|
||||
saveUser(u)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Process the message
|
||||
if m.Channel != "" {
|
||||
// Check if we know what the channel is
|
||||
chnl, err := getChannelInfo(m.Channel)
|
||||
@ -137,30 +143,133 @@ func processMessage(slack *Slack, m *Message) {
|
||||
}
|
||||
}
|
||||
if isDirectMessage {
|
||||
for _, proc := range messageProcessors {
|
||||
if isAdmin(m.User) {
|
||||
proc.ProcessAdminUserMessage(slack, m)
|
||||
if m.User == "U030RD9NU" {
|
||||
// message from br0xen
|
||||
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 {
|
||||
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 {
|
||||
proc.ProcessChannelMessage(slack, m)
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func registerMessageProcessor(b messageProcessor) {
|
||||
// Register a Message Processor
|
||||
// Make sure that we haven't already registered it
|
||||
for _, proc := range messageProcessors {
|
||||
if proc.GetName() == b.GetName() {
|
||||
panic(fmt.Errorf("Attempted to Re-register Message Processor %s", b.GetName()))
|
||||
func getAoCLeaderboard(boardId string) (*Leaderboard, error) {
|
||||
var err error
|
||||
var req *http.Request
|
||||
var content []byte
|
||||
var resp *http.Response
|
||||
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) {
|
||||
|
196
model.go
196
model.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gogs.bullercodeworks.com/brian/boltease"
|
||||
@ -9,44 +10,46 @@ import (
|
||||
func getDatabase() (*boltease.DB, error) {
|
||||
db, err := boltease.Create("aocbot.db", 0644, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return db, err
|
||||
}
|
||||
// Make sure that we have a 'users' bucket
|
||||
db.MkBucketPath([]string{"users"})
|
||||
// Make sure that we have a 'channels' bucket
|
||||
db.MkBucketPath([]string{"channels"})
|
||||
// Make sure that we have a 'aocmembers' bucket
|
||||
db.MkBucketPath([]string{"AoCMembers"})
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func (db *boltease.DB) saveUser(usr *User) error {
|
||||
func saveUser(usr *User) error {
|
||||
var err error
|
||||
if err = db.OpenDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.CloseDB()
|
||||
|
||||
usr := []string{"users", usr.ID}
|
||||
db.SetValue(append(usr, "Name"), usr.Name)
|
||||
db.SetBool(append(usr, "Deleted"), usr.Deleted)
|
||||
db.SetValue(append(usr, "Status"), usr.Status)
|
||||
db.SetValue(append(usr, "Color"), usr.Color)
|
||||
db.SetValue(append(usr, "RealName"), usr.RealName)
|
||||
db.SetValue(append(usr, "TZ"), usr.TZ)
|
||||
db.SetValue(append(usr, "TZLabel"), usr.TZLabel)
|
||||
db.SetInt(append(usr, "TZOffset"), usr.TZOffset)
|
||||
db.SetBool(append(usr, "IsAdmin"), usr.IsAdmin)
|
||||
db.SetBool(append(usr, "IsOwner"), usr.IsOwner)
|
||||
db.SetBool(append(usr, "IsPrimaryOwner"), usr.IsPrimaryOwner)
|
||||
db.SetBool(append(usr, "IsRestricted"), usr.IsRestricted)
|
||||
db.SetBool(append(usr, "IsUltraRestricted"), usr.IsUltraRestricted)
|
||||
db.SetBool(append(usr, "IsBot"), usr.IsBot)
|
||||
db.SetBool(append(usr, "HasFiles"), usr.HasFiles)
|
||||
db.SetTimestamp(append(usr, "LastUpdated"), time.Now())
|
||||
usrPath := []string{"users", usr.ID}
|
||||
db.SetValue(usrPath, "Name", usr.Name)
|
||||
db.SetBool(usrPath, "Deleted", usr.Deleted)
|
||||
db.SetValue(usrPath, "Status", usr.Status)
|
||||
db.SetValue(usrPath, "Color", usr.Color)
|
||||
db.SetValue(usrPath, "RealName", usr.RealName)
|
||||
db.SetValue(usrPath, "TZ", usr.TZ)
|
||||
db.SetValue(usrPath, "TZLabel", usr.TZLabel)
|
||||
db.SetInt(usrPath, "TZOffset", usr.TZOffset)
|
||||
db.SetBool(usrPath, "IsAdmin", usr.IsAdmin)
|
||||
db.SetBool(usrPath, "IsOwner", usr.IsOwner)
|
||||
db.SetBool(usrPath, "IsPrimaryOwner", usr.IsPrimaryOwner)
|
||||
db.SetBool(usrPath, "IsRestricted", usr.IsRestricted)
|
||||
db.SetBool(usrPath, "IsUltraRestricted", usr.IsUltraRestricted)
|
||||
db.SetBool(usrPath, "IsBot", usr.IsBot)
|
||||
db.SetBool(usrPath, "HasFiles", usr.HasFiles)
|
||||
db.SetTimestamp(usrPath, "LastUpdated", time.Now())
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *boltease.DB) getUser(usrId string) (*User, error) {
|
||||
func getUser(usrId string) (*User, error) {
|
||||
usr := new(User)
|
||||
var err error
|
||||
if err = db.OpenDB(); err != nil {
|
||||
@ -54,53 +57,172 @@ func (db *boltease.DB) getUser(usrId string) (*User, error) {
|
||||
}
|
||||
defer db.CloseDB()
|
||||
|
||||
upath := []string{"users", usrID}
|
||||
if usr.Name, err = db.GetValue(append(upath, "Name")); err != nil {
|
||||
upath := []string{"users", usrId}
|
||||
if usr.Name, err = db.GetValue(upath, "Name"); err != nil {
|
||||
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
|
||||
}
|
||||
if usr.Status, err = db.GetValue(append(upath, "Status")); err != nil {
|
||||
if usr.Status, err = db.GetValue(upath, "Status"); err != nil {
|
||||
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
|
||||
}
|
||||
if usr.RealName, err = db.GetValue(append(upath, "RealName")); err != nil {
|
||||
if usr.RealName, err = db.GetValue(upath, "RealName"); err != nil {
|
||||
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
|
||||
}
|
||||
if usr.TZLabel, err = db.GetValue(append(upath, "TZLabel")); err != nil {
|
||||
if usr.TZLabel, err = db.GetValue(upath, "TZLabel"); err != nil {
|
||||
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
|
||||
}
|
||||
if usr.IsAdmin, err = db.GetBool(append(upath, "IsAdmin")); err != nil {
|
||||
if usr.IsAdmin, err = db.GetBool(upath, "IsAdmin"); err != nil {
|
||||
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
|
||||
}
|
||||
if usr.IsPrimaryOwner, err = db.GetBool(append(upath, "IsPrimaryOwner")); err != nil {
|
||||
if usr.IsPrimaryOwner, err = db.GetBool(upath, "IsPrimaryOwner"); err != nil {
|
||||
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
|
||||
}
|
||||
if usr.IsUltraRestricted, err = db.GetBool(append(upath, "IsUltraRestricted")); err != nil {
|
||||
if usr.IsUltraRestricted, err = db.GetBool(upath, "IsUltraRestricted"); err != nil {
|
||||
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
|
||||
}
|
||||
if usr.HasFiles, err = db.GetBool(append(upath, "HasFiles")); err != nil {
|
||||
if usr.HasFiles, err = db.GetBool(upath, "HasFiles"); err != nil {
|
||||
return usr, err
|
||||
}
|
||||
usr.LastUpdated, err = db.GetTimestamp(append(upath, "LastUpdated"))
|
||||
usr.LastUpdated, err = db.GetTimestamp(upath, "LastUpdated")
|
||||
|
||||
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"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@ -91,7 +93,15 @@ func (s *Slack) getMessage() (Message, error) {
|
||||
|
||||
func (s *Slack) sendMessageToUser(uid string, msg string) error {
|
||||
// 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) {
|
||||
@ -122,6 +132,41 @@ func (s *Slack) getUserInfo(uid string) (*User, error) {
|
||||
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) {
|
||||
url := fmt.Sprintf("https://slack.com/api/channels.info?token=%s&channel=%s", s.apiToken, cid)
|
||||
resp, err := http.Get(url)
|
||||
@ -153,34 +198,6 @@ func (s *Slack) getChannelInfo(cid string) (*Channel, error) {
|
||||
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
|
||||
// Only some fields are included. The rest are ignored by json.Unmarshal.
|
||||
type responseRtmStart struct {
|
||||
@ -205,3 +222,19 @@ type responseChannelLookup struct {
|
||||
Error string `json:"error"`
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
Reference in New Issue
Block a user