2015-10-28 16:46:45 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
2015-10-29 16:16:25 +00:00
|
|
|
"strings"
|
2015-10-28 16:46:45 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const programName = "statbot"
|
|
|
|
|
2015-10-29 16:16:25 +00:00
|
|
|
type messageProcessor interface {
|
|
|
|
GetName() string
|
|
|
|
GetHelp() string
|
|
|
|
ProcessMessage(s *Slack, m *Message)
|
|
|
|
ProcessAdminMessage(s *Slack, m *Message)
|
2015-10-29 19:16:45 +00:00
|
|
|
ProcessBotMessage(s *Slack, m *Message)
|
2015-10-29 16:16:25 +00:00
|
|
|
ProcessUserMessage(s *Slack, m *Message)
|
|
|
|
ProcessAdminUserMessage(s *Slack, m *Message)
|
2015-10-29 19:16:45 +00:00
|
|
|
ProcessBotUserMessage(s *Slack, m *Message)
|
2015-10-29 16:16:25 +00:00
|
|
|
ProcessChannelMessage(s *Slack, m *Message)
|
|
|
|
ProcessAdminChannelMessage(s *Slack, m *Message)
|
2015-10-29 19:16:45 +00:00
|
|
|
ProcessBotChannelMessage(s *Slack, m *Message)
|
2015-10-29 16:16:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var messageProcessors []messageProcessor
|
|
|
|
|
|
|
|
type statProcessor interface {
|
|
|
|
GetName() string
|
|
|
|
GetStatKeys() []string
|
|
|
|
ProcessMessage(m *Message)
|
2015-10-29 19:16:45 +00:00
|
|
|
ProcessBotMessage(m *Message)
|
2015-10-29 16:16:25 +00:00
|
|
|
ProcessUserMessage(m *Message)
|
2015-10-29 19:16:45 +00:00
|
|
|
ProcessBotUserMessage(m *Message)
|
2015-10-29 16:16:25 +00:00
|
|
|
ProcessChannelMessage(m *Message)
|
2015-10-29 19:16:45 +00:00
|
|
|
ProcessBotChannelMessage(m *Message)
|
2015-10-29 16:16:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var statProcessors []statProcessor
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
func main() {
|
|
|
|
if len(os.Args) != 2 {
|
|
|
|
fmt.Fprintf(os.Stderr, "usage: statbot slack-bot-token\n")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
2015-10-29 16:16:25 +00:00
|
|
|
var slack *Slack
|
2015-10-28 16:46:45 +00:00
|
|
|
|
|
|
|
if err = initDatabase(); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if slack, err = CreateSlack(os.Args[1]); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2015-10-30 13:10:05 +00:00
|
|
|
statWebMain(slack)
|
2015-10-29 16:16:25 +00:00
|
|
|
statBotMain(slack)
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is the main function for the statbot
|
|
|
|
func statBotMain(slack *Slack) {
|
|
|
|
// start a websocket-based Real Time API session
|
|
|
|
registerStatProcessor(new(levelUpStatProcessor))
|
|
|
|
registerStatProcessor(new(generalStatProcessor))
|
|
|
|
|
2015-11-06 01:36:09 +00:00
|
|
|
levelUpAchievements := new(levelUpAchieveStatProcessor)
|
|
|
|
// Register Achievements
|
|
|
|
|
2015-10-29 16:16:25 +00:00
|
|
|
registerMessageProcessor(new(generalProcessor))
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
fmt.Println("statbot ready, ^C exits")
|
|
|
|
|
2015-10-29 16:16:25 +00:00
|
|
|
writeToLog("== " + time.Now().Format(time.RFC3339) + " - Bot Started ==\n")
|
2015-10-28 16:46:45 +00:00
|
|
|
for {
|
|
|
|
// read each incoming message
|
|
|
|
m, err := slack.getMessage()
|
2015-10-29 16:16:25 +00:00
|
|
|
if err == nil {
|
2015-11-06 01:36:09 +00:00
|
|
|
writeToLog(" " + time.Now().Format(time.RFC3339) + " - Received Message\n")
|
2015-10-29 16:16:25 +00:00
|
|
|
processMessage(slack, &m)
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
|
|
|
}
|
2015-10-29 16:16:25 +00:00
|
|
|
writeToLog("== " + time.Now().Format(time.RFC3339) + " - Bot Stopped ==\n\n")
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func processMessage(slack *Slack, m *Message) {
|
2015-10-29 19:16:45 +00:00
|
|
|
/*
|
|
|
|
if mb, me := json.Marshal(m); me == nil {
|
|
|
|
// Write the JSON representation to the log
|
|
|
|
writeToLog(string(mb) + "\n")
|
|
|
|
}
|
|
|
|
*/
|
2015-10-28 16:46:45 +00:00
|
|
|
|
2015-11-05 12:55:49 +00:00
|
|
|
if m.Type == "message" || m.Type == "reaction_added" {
|
2015-10-29 19:16:45 +00:00
|
|
|
var err error
|
|
|
|
var usr *User
|
2015-10-29 16:16:25 +00:00
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
// Check if we know who the user is
|
2015-10-29 19:16:45 +00:00
|
|
|
usr, err = getUserInfo(m.User)
|
2015-10-29 16:16:25 +00:00
|
|
|
// If the user information hasn't been updated in the last day, update it.
|
2015-10-28 16:46:45 +00:00
|
|
|
if err != nil || usr.LastUpdated.IsZero() || time.Since(usr.LastUpdated) > (time.Hour*24) {
|
|
|
|
if u, ue := slack.getUserInfo(m.User); ue == nil {
|
|
|
|
saveUserInfo(u)
|
|
|
|
}
|
|
|
|
}
|
2015-10-29 19:16:45 +00:00
|
|
|
|
|
|
|
for _, stats := range statProcessors {
|
2015-10-30 14:33:18 +00:00
|
|
|
if usr.IsBot {
|
|
|
|
stats.ProcessBotMessage(m)
|
|
|
|
} else {
|
|
|
|
stats.ProcessMessage(m)
|
2015-10-29 19:16:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, proc := range messageProcessors {
|
|
|
|
if isAdmin(m.User) {
|
|
|
|
proc.ProcessAdminMessage(slack, m)
|
|
|
|
}
|
|
|
|
if usr.IsBot {
|
|
|
|
proc.ProcessBotMessage(slack, m)
|
|
|
|
} else {
|
|
|
|
proc.ProcessMessage(slack, m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-28 16:46:45 +00:00
|
|
|
if m.Channel != "" {
|
|
|
|
// Check if we know what the channel is
|
|
|
|
chnl, err := getChannelInfo(m.Channel)
|
2015-10-29 19:16:45 +00:00
|
|
|
var isDirectMessage bool
|
|
|
|
|
2015-10-29 16:16:25 +00:00
|
|
|
// If the channel information hasn't been updated in the last day, update it.
|
2015-10-28 16:46:45 +00:00
|
|
|
if err != nil || chnl.LastUpdated.IsZero() || time.Since(chnl.LastUpdated) > (time.Hour*24) {
|
|
|
|
// Either we don't have this channel, or it's a direct message
|
2015-10-29 19:16:45 +00:00
|
|
|
if c, ce := slack.getChannelInfo(m.Channel); ce == nil {
|
2015-10-28 16:46:45 +00:00
|
|
|
// Save channel info
|
|
|
|
saveChannelInfo(c)
|
2015-10-29 19:16:45 +00:00
|
|
|
} else {
|
|
|
|
isDirectMessage = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if isDirectMessage {
|
|
|
|
// Invalid channel, save as a direct message
|
|
|
|
saveUserMessage(m.User, m)
|
|
|
|
for _, stats := range statProcessors {
|
|
|
|
stats.ProcessUserMessage(m)
|
|
|
|
}
|
|
|
|
for _, proc := range messageProcessors {
|
|
|
|
if isAdmin(m.User) {
|
|
|
|
proc.ProcessAdminUserMessage(slack, m)
|
2015-10-29 16:16:25 +00:00
|
|
|
}
|
2015-10-29 19:16:45 +00:00
|
|
|
proc.ProcessUserMessage(slack, m)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// And save the channel message
|
|
|
|
err = saveChannelMessage(m.Channel, m)
|
|
|
|
|
|
|
|
for _, proc := range statProcessors {
|
|
|
|
proc.ProcessChannelMessage(m)
|
|
|
|
}
|
|
|
|
for _, proc := range messageProcessors {
|
|
|
|
proc.ProcessChannelMessage(slack, m)
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-29 16:16:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
messageProcessors = append(messageProcessors, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
func registerStatProcessor(b statProcessor) {
|
|
|
|
// Register a Statistic Processor
|
|
|
|
// First make sure that we don't have any 'key' collisions
|
|
|
|
for _, proc := range statProcessors {
|
|
|
|
for _, testKey := range proc.GetStatKeys() {
|
|
|
|
for _, k := range b.GetStatKeys() {
|
|
|
|
if strings.Replace(testKey, "*", "", -1) == strings.Replace(k, "*", "", -1) {
|
|
|
|
panic(fmt.Errorf("Stat Key Collision (%s=>%s and %s=>%s)",
|
|
|
|
b.GetName(), k,
|
|
|
|
proc.GetName(), testKey,
|
|
|
|
))
|
|
|
|
}
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
2015-10-29 16:16:25 +00:00
|
|
|
}
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
2015-10-29 16:16:25 +00:00
|
|
|
statProcessors = append(statProcessors, b)
|
2015-10-28 16:46:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func writeToLog(d string) {
|
|
|
|
f, err := os.OpenFile("statbot.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0664)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
f.WriteString(d)
|
|
|
|
f.Close()
|
|
|
|
}
|