223 lines
5.0 KiB
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{})
|
|
}
|