spoc-bot-vr/.message.go

223 lines
5.0 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
)
var (
ErrIrrelevantMessage = errors.New("message isnt relevant to spoc bot vr")
)
type Message struct {
ID string
TS uint64
Source string
Channel string
Thread string
EventName string
Event string
Plaintext string
Asset string
Resolved bool
Datacenter string
}
func (m Message) Empty() bool {
return m == (Message{})
}
func (m Message) Time() time.Time {
return time.Unix(int64(m.TS), 0)
}
func (m Message) Serialize() []byte {
b, err := json.Marshal(m)
if err != nil {
panic(err)
}
return b
}
func MustDeserialize(b []byte) Message {
m, err := Deserialize(b)
if err != nil {
panic(err)
}
return m
}
func Deserialize(b []byte) (Message, error) {
var m Message
err := json.Unmarshal(b, &m)
return m, err
}
type (
slackMessage struct {
slackEvent
Type string
TS uint64 `json:"event_time"`
Event slackEvent
MessageTS string `json:"ts"`
}
slackEvent struct {
ID string `json:"event_ts"`
Channel string
// rewrites
Nested *slackEvent `json:"message"`
PreviousMessage *slackEvent `json:"previous_message"`
// human
ParentID string `json:"thread_ts"`
Text string
Blocks []slackBlock
// bot
Bot slackBot `json:"bot_profile"`
Attachments []slackAttachment
}
slackBlock struct {
Elements []slackElement
}
slackElement struct {
Elements []slackElement
RichText string `json:"text"`
}
slackBot struct {
Name string
}
slackAttachment struct {
Color string
Title string
Text string
Fields []slackField
Actions []slackAction
}
slackField struct {
Value string
Title string
}
slackAction struct{}
)
func ParseSlack(b []byte, assetPattern, datacenterPattern, eventNamePattern string) (Message, error) {
return ParseSlackFromChannel(b, assetPattern, datacenterPattern, eventNamePattern, "")
}
func ParseSlackFromChannel(b []byte, assetPattern, datacenterPattern, eventNamePattern string, ch string) (Message, error) {
m, err := parseSlackJSON(b, ch)
if err != nil {
return Message{}, err
}
for pattern, ptr := range map[string]*string{
assetPattern: &m.Asset,
datacenterPattern: &m.Datacenter,
eventNamePattern: &m.EventName,
} {
r := regexp.MustCompile(pattern)
parsed := r.FindString(*ptr)
for i, name := range r.SubexpNames() {
if i > 0 && name != "" {
parsed = r.FindStringSubmatch(*ptr)[i]
}
}
*ptr = parsed
}
return m, nil
}
func parseSlackJSON(b []byte, ch string) (Message, error) {
s, err := _parseSlackJSON(b)
if err != nil {
return Message{}, err
}
if ch != "" {
s.Event.Channel = ch
}
if s.Event.Bot.Name != "" {
if len(s.Event.Attachments) == 0 {
return Message{}, ErrIrrelevantMessage
} else if !strings.Contains(s.Event.Attachments[0].Title, ": Firing: ") {
return Message{}, ErrIrrelevantMessage
}
var tagsField string
for _, field := range s.Event.Attachments[0].Fields {
if field.Title == "Tags" {
tagsField = field.Value
}
}
return Message{
ID: fmt.Sprintf("%s/%v", s.Event.ID, s.TS),
TS: s.TS,
Source: fmt.Sprintf(`https://renderinc.slack.com/archives/%s/p%s`, s.Event.Channel, strings.ReplaceAll(s.Event.ID, ".", "")),
Channel: s.Event.Channel,
Thread: s.Event.ID,
EventName: strings.Split(s.Event.Attachments[0].Title, ": Firing: ")[1],
Event: strings.TrimPrefix(strings.Split(s.Event.Attachments[0].Title, ":")[0], "#"),
Plaintext: s.Event.Attachments[0].Text,
Asset: s.Event.Attachments[0].Text,
Resolved: !strings.HasPrefix(s.Event.Attachments[0].Color, "F"),
Datacenter: tagsField,
}, nil
}
if s.Event.ParentID == "" {
return Message{}, ErrIrrelevantMessage
}
return Message{
ID: fmt.Sprintf("%s/%v", s.Event.ParentID, s.TS),
TS: s.TS,
Source: fmt.Sprintf(`https://renderinc.slack.com/archives/%s/p%s`, s.Event.Channel, strings.ReplaceAll(s.Event.ParentID, ".", "")),
Channel: s.Event.Channel,
Thread: s.Event.ParentID,
EventName: "",
Event: "",
Plaintext: s.Event.Text,
Asset: "",
Datacenter: "",
}, nil
}
func _parseSlackJSON(b []byte) (slackMessage, error) {
var result slackMessage
err := json.Unmarshal(b, &result)
switch result.Type {
case "message":
result.Event = result.slackEvent
result.TS, _ = strconv.ParseUint(strings.Split(result.MessageTS, ".")[0], 10, 64)
result.Event.ID = result.MessageTS
}
if result.Event.Nested != nil && !result.Event.Nested.Empty() {
result.Event.Blocks = result.Event.Nested.Blocks
result.Event.Bot = result.Event.Nested.Bot
result.Event.Attachments = result.Event.Nested.Attachments
result.Event.Nested = nil
}
if result.Event.PreviousMessage != nil {
if result.Event.PreviousMessage.ID != "" {
result.Event.ID = result.Event.PreviousMessage.ID
}
result.Event.PreviousMessage = nil
}
return result, err
}
func (this slackEvent) Empty() bool {
return fmt.Sprintf("%+v", this) == fmt.Sprintf("%+v", slackEvent{})
}