package main import ( "encoding/json" "errors" "fmt" "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 } 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 { TS uint64 `json:"event_time"` Event slackEvent } 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) (Message, error) { s, err := parseSlack(b) if err != nil { return Message{}, err } 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 } 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.Split(s.Event.Attachments[0].Title, ":")[0], Plaintext: s.Event.Attachments[0].Text, Asset: "TODO", Resolved: !strings.HasPrefix(s.Event.Attachments[0].Color, "F"), }, nil } 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: "TODO", Event: "TODO", Plaintext: s.Event.Text, Asset: "TODO", }, nil } func parseSlack(b []byte) (slackMessage, error) { var result slackMessage err := json.Unmarshal(b, &result) 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{}) }