Compare commits

..

14 Commits

Author SHA1 Message Date
Bel LaPointe
02331752fe ID scoped to thread and dont out of bounds except 2024-04-12 10:05:25 -06:00
Bel LaPointe
0d3910829d if $POSTGRES_CONN set then use it 2024-04-12 10:00:13 -06:00
Bel LaPointe
8f288cf12e impl Postgres driver 2024-04-12 09:58:32 -06:00
Bel LaPointe
1b148092b9 on slack webhook storage.Upsert message 2024-04-12 09:22:42 -06:00
Bel LaPointe
24628f4ebb Config has a .storage, .queue, .driiver 2024-04-12 09:21:36 -06:00
Bel LaPointe
c51e580e09 impl RAM db 2024-04-12 09:19:48 -06:00
Bel LaPointe
82fc7526d0 main parses incoming slack event 2024-04-12 09:15:16 -06:00
Bel LaPointe
02fecb7eb2 little things all tested 2024-04-12 09:13:48 -06:00
Bel LaPointe
6ba23e61c3 make helper NewTestDB more helpful with t.TempDir 2024-04-12 09:10:23 -06:00
Bel LaPointe
8883856a63 test driver 2024-04-12 09:06:57 -06:00
Bel LaPointe
634255725a test q 2024-04-12 09:02:08 -06:00
Bel LaPointe
233ba8ae2b test parsing slack messages 2024-04-12 08:57:17 -06:00
Bel LaPointe
ccee4a49da break slack_events.json into many files for testing 2024-04-12 08:08:59 -06:00
Bel LaPointe
4080af952d oooone more sample 2024-04-12 08:05:38 -06:00
23 changed files with 1451 additions and 33 deletions

View File

@@ -1,6 +1,7 @@
package main
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -8,6 +9,7 @@ import (
"slices"
"strconv"
"strings"
"time"
)
type Config struct {
@@ -16,13 +18,17 @@ type Config struct {
InitializeSlack bool
SlackToken string
SlackChannels string
PostgresConn string
storage Storage
queue Queue
driver Driver
}
func newConfig() (Config, error) {
return newConfigFromEnv(os.Getenv)
func newConfig(ctx context.Context) (Config, error) {
return newConfigFromEnv(ctx, os.Getenv)
}
func newConfigFromEnv(getEnv func(string) string) (Config, error) {
func newConfigFromEnv(ctx context.Context, getEnv func(string) string) (Config, error) {
def := Config{
Port: 8080,
}
@@ -73,5 +79,19 @@ func newConfigFromEnv(getEnv func(string) string) (Config, error) {
} else if err := json.Unmarshal(b, &result); err != nil {
return Config{}, err
}
result.driver = NewRAM()
if result.PostgresConn != "" {
ctx, can := context.WithTimeout(ctx, time.Second*10)
defer can()
pg, err := NewPostgres(ctx, result.PostgresConn)
if err != nil {
return Config{}, err
}
result.driver = pg
}
result.storage = NewStorage(result.driver)
result.queue = NewQueue(result.driver)
return result, nil
}

View File

@@ -1,9 +1,12 @@
package main
import "testing"
import (
"context"
"testing"
)
func TestNewConfig(t *testing.T) {
if got, err := newConfigFromEnv(func(k string) string {
if got, err := newConfigFromEnv(context.Background(), func(k string) string {
t.Logf("getenv(%s)", k)
switch k {
case "PORT":

195
driver.go
View File

@@ -2,11 +2,17 @@ package main
import (
"context"
"database/sql"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"sync"
"time"
"go.etcd.io/bbolt"
_ "github.com/lib/pq"
)
type Driver interface {
@@ -16,16 +22,197 @@ type Driver interface {
Set(context.Context, string, string, []byte) error
}
type Postgres struct {
db *sql.DB
}
func NewPostgres(ctx context.Context, conn string) (Postgres, error) {
db, err := sql.Open("postgres", conn)
if err != nil {
return Postgres{}, err
}
pg := Postgres{db: db}
if err := pg.setup(ctx); err != nil {
pg.Close()
return Postgres{}, fmt.Errorf("failed setup: %w", err)
}
return pg, nil
}
func (pg Postgres) setup(ctx context.Context) error {
tableQ, err := pg.table("q")
if err != nil {
return err
}
tableM, err := pg.table("m")
if err != nil {
return err
}
if _, err := pg.db.ExecContext(ctx, fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id TEXT NOT NULL,
v JSONB NOT NULL
);
CREATE TABLE IF NOT EXISTS %s (
id TEXT NOT NULL,
v JSONB NOT NULL
);
ALTER TABLE %s DROP CONSTRAINT IF EXISTS %s_id_unique;
ALTER TABLE %s ADD CONSTRAINT %s_id_unique UNIQUE (id);
ALTER TABLE %s DROP CONSTRAINT IF EXISTS %s_id_unique;
ALTER TABLE %s ADD CONSTRAINT %s_id_unique UNIQUE (id);
`, tableQ,
tableM,
tableQ, tableQ,
tableQ, tableQ,
tableM, tableM,
tableM, tableM,
)); err != nil {
return err
}
return nil
}
func (pg Postgres) table(s string) (string, error) {
switch s {
case "q":
return "spoc_bot_vr_q", nil
case "m":
return "spoc_bot_vr_messages", nil
}
return "", errors.New("invalid table " + s)
}
func (pg Postgres) Close() error {
return pg.db.Close()
}
func (pg Postgres) ForEach(ctx context.Context, ns string, cb func(string, []byte) error) error {
table, err := pg.table(ns)
if err != nil {
return err
}
rows, err := pg.db.QueryContext(ctx, fmt.Sprintf(`SELECT id, v FROM %s;`, table))
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var id string
var v []byte
if err := rows.Scan(&id, &v); err != nil {
return err
} else if err := cb(id, v); err != nil {
return err
}
}
return ctx.Err()
}
func (pg Postgres) Get(ctx context.Context, ns, id string) ([]byte, error) {
table, err := pg.table(ns)
if err != nil {
return nil, err
}
row := pg.db.QueryRowContext(ctx, fmt.Sprintf(`SELECT v FROM %s WHERE id='%s';`, table, id))
if err := row.Err(); err != nil {
return nil, err
}
var v []byte
if err := row.Scan(&v); err != nil && !errors.Is(err, sql.ErrNoRows) {
return nil, err
}
return v, nil
}
func (pg Postgres) Set(ctx context.Context, ns, id string, v []byte) error {
table, err := pg.table(ns)
if err != nil {
return err
}
if v == nil {
_, err = pg.db.ExecContext(ctx, fmt.Sprintf(`DELETE FROM %s WHERE id='%s';`, table, id))
return err
}
_, err = pg.db.ExecContext(ctx, fmt.Sprintf(`INSERT INTO %s (id, v) VALUES ('%s', '%s') ON CONFLICT (id) DO UPDATE SET v = '%s'`, table, id, v, v))
return err
}
type RAM struct {
m map[string]map[string][]byte
lock *sync.RWMutex
}
func NewRAM() RAM {
return RAM{
m: make(map[string]map[string][]byte),
lock: &sync.RWMutex{},
}
}
func (ram RAM) Close() error {
return nil
}
func (ram RAM) ForEach(ctx context.Context, ns string, cb func(string, []byte) error) error {
ram.lock.RLock()
defer ram.lock.RUnlock()
for k, v := range ram.m[ns] {
if ctx.Err() != nil {
break
}
if err := cb(k, v); err != nil {
return err
}
}
return ctx.Err()
}
func (ram RAM) Get(_ context.Context, ns, id string) ([]byte, error) {
ram.lock.RLock()
defer ram.lock.RUnlock()
if _, ok := ram.m[ns]; !ok {
return nil, nil
}
return ram.m[ns][id], nil
}
func (ram RAM) Set(_ context.Context, ns, id string, v []byte) error {
ram.lock.Lock()
defer ram.lock.Unlock()
if _, ok := ram.m[ns]; !ok {
ram.m[ns] = map[string][]byte{}
}
ram.m[ns][id] = v
return nil
}
type BBolt struct {
db *bbolt.DB
}
func NewTestDB() BBolt {
d, err := ioutil.TempDir(os.TempDir(), "test-db-*")
func NewTestDBIn(d string) BBolt {
d, err := ioutil.TempDir(d, "test-db-*")
if err != nil {
panic(err)
}
db, err := NewDB(d)
db, err := NewDB(path.Join(d, "bb"))
if err != nil {
panic(err)
}

84
driver_test.go Normal file
View File

@@ -0,0 +1,84 @@
package main
import (
"context"
"errors"
"io"
"os"
"testing"
"time"
)
func TestPostgres(t *testing.T) {
ctx, can := context.WithTimeout(context.Background(), time.Second*15)
defer can()
conn := os.Getenv("INTEGRATION_POSTGRES_CONN")
if conn == "" {
t.Skip()
}
pg, err := NewPostgres(ctx, conn)
if err != nil {
t.Fatal(err)
}
testDriver(t, pg)
}
func TestDriverRAM(t *testing.T) {
testDriver(t, NewRAM())
}
func TestDriverBBolt(t *testing.T) {
testDriver(t, NewTestDBIn(t.TempDir()))
}
func testDriver(t *testing.T, d Driver) {
ctx, can := context.WithTimeout(context.Background(), time.Second*15)
defer can()
defer d.Close()
if b, err := d.Get(ctx, "m", "id"); err != nil {
t.Error("cannot get from empty:", err)
} else if b != nil {
t.Error("got fake from empty")
}
if err := d.ForEach(ctx, "m", func(string, []byte) error {
return errors.New("should have no hits")
}); err != nil {
t.Error("failed to forEach empty:", err)
}
if err := d.Set(ctx, "m", "id", []byte(`"hello world"`)); err != nil {
t.Error("cannot set from empty:", err)
}
if b, err := d.Get(ctx, "m", "id"); err != nil {
t.Error("cannot get from full:", err)
} else if string(b) != `"hello world"` {
t.Error("got fake from full")
}
if err := d.ForEach(ctx, "m", func(id string, v []byte) error {
if id != "id" {
t.Error("for each id weird:", id)
}
if string(v) != `"hello world"` {
t.Error("for each value weird:", string(v))
}
return io.EOF
}); err != io.EOF {
t.Error("failed to forEach full:", err)
}
if err := d.Set(ctx, "m", "id", nil); err != nil {
t.Error("cannot set from full:", err)
}
if b, err := d.Get(ctx, "m", "id"); err != nil {
t.Error("cannot get from deleted:", err)
} else if b != nil {
t.Error("got fake from deleted")
}
}

5
go.mod
View File

@@ -7,4 +7,7 @@ require (
go.etcd.io/bbolt v1.3.9
)
require golang.org/x/sys v0.4.0 // indirect
require (
github.com/lib/pq v1.10.9 // indirect
golang.org/x/sys v0.4.0 // indirect
)

2
go.sum
View File

@@ -1,5 +1,7 @@
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=

15
main.go
View File

@@ -19,10 +19,11 @@ func main() {
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
defer can()
cfg, err := newConfig()
cfg, err := newConfig(ctx)
if err != nil {
panic(err)
}
defer cfg.driver.Close()
if err := run(ctx, cfg); err != nil && ctx.Err() == nil {
panic(err)
@@ -115,7 +116,15 @@ func _newHandlerPostAPIV1EventsSlack(cfg Config) http.HandlerFunc {
return
}
log.Printf("slack event: %s", b)
http.Error(w, "not impl", http.StatusNotImplemented)
m, err := ParseSlack(b)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := cfg.storage.Upsert(r.Context(), m); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}

View File

@@ -1,6 +1,11 @@
package main
import "encoding/json"
import (
"encoding/json"
"errors"
"fmt"
"strings"
)
type Message struct {
ID string
@@ -39,3 +44,94 @@ func Deserialize(b []byte) (Message, error) {
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
// 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{}, errors.New("bot message has no attachments")
} else if !strings.Contains(s.Event.Attachments[0].Title, ": Firing: ") {
return Message{}, errors.New("bot message attachment is not Firing")
}
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",
}, 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)
return result, err
}

113
message_test.go Normal file
View File

@@ -0,0 +1,113 @@
package main
import (
"fmt"
"os"
"path"
"testing"
)
func TestParseSlackTestdata(t *testing.T) {
cases := map[string]struct {
slackMessage slackMessage
message Message
}{
"human_thread_message_from_opsgenie_alert.json": {
slackMessage: slackMessage{
TS: 1712930706,
Event: slackEvent{
ID: "1712930706.598629",
Channel: "C06U1DDBBU4",
ParentID: "1712927439.728409",
Text: "I gotta do this",
Blocks: []slackBlock{{
Elements: []slackElement{{
Elements: []slackElement{{
RichText: "I gotta do this",
}},
}},
}},
Bot: slackBot{
Name: "",
},
Attachments: []slackAttachment{},
},
},
message: Message{
ID: "1712927439.728409/1712930706",
TS: 1712930706,
Source: "https://renderinc.slack.com/archives/C06U1DDBBU4/p1712927439728409",
Channel: "C06U1DDBBU4",
Thread: "1712927439.728409",
EventName: "TODO",
Event: "TODO",
Plaintext: "I gotta do this",
Asset: "TODO",
},
},
"opsgenie_alert.json": {
slackMessage: slackMessage{
TS: 1712927439,
Event: slackEvent{
ID: "1712927439.728409",
Channel: "C06U1DDBBU4",
Bot: slackBot{
Name: "Opsgenie for Alert Management",
},
Attachments: []slackAttachment{{
Color: "F4511E",
Title: "#11071: [Grafana]: Firing: Alertconfig Workflow Failed",
Text: "At least one alertconfig run has failed unexpectedly.\nDashboard: <https://grafana.render.com/d/VLZU83YVk?orgId=1>\nPanel: <https://grafana.render.com/d/VLZU83YVk?orgId=1&amp;viewPanel=17>\nSource: <https://grafana.render.com/alerting/grafana/fa7b06b8-b4d8-4979-bce7-5e1c432edd81/view?orgId=1>",
Fields: []slackField{
{Value: "P3", Title: "Priority"},
{Value: "alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb", Title: "Tags"},
{Value: "Datastores Non-Critical", Title: "Routed Teams"},
},
Actions: []slackAction{{}, {}, {}},
}},
},
},
message: Message{
ID: "1712927439.728409/1712927439",
TS: 1712927439,
Source: "https://renderinc.slack.com/archives/C06U1DDBBU4/p1712927439728409",
Channel: "C06U1DDBBU4",
Thread: "1712927439.728409",
EventName: "Alertconfig Workflow Failed",
Event: "#11071",
Plaintext: "At least one alertconfig run has failed unexpectedly.\nDashboard: <https://grafana.render.com/d/VLZU83YVk?orgId=1>\nPanel: <https://grafana.render.com/d/VLZU83YVk?orgId=1&amp;viewPanel=17>\nSource: <https://grafana.render.com/alerting/grafana/fa7b06b8-b4d8-4979-bce7-5e1c432edd81/view?orgId=1>",
Asset: "TODO",
},
},
}
for name, d := range cases {
want := d
t.Run(name, func(t *testing.T) {
b, err := os.ReadFile(path.Join("testdata", "slack_events", name))
if err != nil {
t.Fatal(err)
}
t.Run("parseSlack", func(t *testing.T) {
got, err := parseSlack(b)
if err != nil {
t.Fatal(err)
}
if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", want.slackMessage) {
t.Errorf("wanted \n\t%+v, got\n\t%+v", want.slackMessage, got)
}
})
t.Run("ParseSlack", func(t *testing.T) {
got, err := ParseSlack(b)
if err != nil {
t.Fatal(err)
}
if got != want.message {
t.Errorf("wanted \n\t%+v, got\n\t%+v", want.message, got)
}
})
})
}
}

View File

@@ -11,10 +11,6 @@ type Queue struct {
driver Driver
}
func NewTestQueue() Queue {
return Queue{driver: NewTestDB()}
}
func NewQueue(driver Driver) Queue {
return Queue{driver: driver}
}

37
queue_test.go Normal file
View File

@@ -0,0 +1,37 @@
package main
import (
"context"
"strconv"
"testing"
"time"
)
func TestQueue(t *testing.T) {
ctx, can := context.WithTimeout(context.Background(), time.Second*10)
defer can()
db := NewTestDBIn(t.TempDir())
defer db.Close()
q := NewQueue(db)
for i := 0; i < 39; i++ {
if err := q.Push(ctx, Message{ID: strconv.Itoa(i), TS: uint64(i)}); err != nil {
t.Fatal(i, err)
}
}
found := map[uint64]struct{}{}
for i := 0; i < 39; i++ {
if m, err := q.PeekFirst(ctx); err != nil {
t.Fatal(i, err)
} else if _, ok := found[m.TS]; ok {
t.Error(i, m.TS)
} else if err := q.Ack(ctx, m.ID); err != nil {
t.Fatal(i, err)
} else {
found[m.TS] = struct{}{}
}
}
}

View File

@@ -13,10 +13,6 @@ type Storage struct {
driver Driver
}
func NewTestStorage() Storage {
return Storage{driver: NewTestDB()}
}
func NewStorage(driver Driver) Storage {
return Storage{driver: driver}
}

36
storage_test.go Normal file
View File

@@ -0,0 +1,36 @@
package main
import (
"context"
"testing"
"time"
)
func TestStorage(t *testing.T) {
ctx, can := context.WithTimeout(context.Background(), time.Second)
defer can()
db := NewTestDBIn(t.TempDir())
defer db.Close()
s := NewStorage(db)
if _, err := s.Get(ctx, "id"); err != ErrNotFound {
t.Error("failed to get 404", err)
}
m := Message{
ID: "id",
TS: 1,
}
if err := s.Upsert(ctx, m); err != nil {
t.Error("failed to upsert", err)
}
if m2, err := s.Get(ctx, "id"); err != nil {
t.Error("failed to get", err)
} else if m != m2 {
t.Error(m2)
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,50 @@
{
"hint": "a message",
"token": "redacted",
"team_id": "T9RQLQ0KV",
"context_team_id": "T9RQLQ0KV",
"context_enterprise_id": null,
"api_app_id": "A06TYH7CALB",
"event": {
"user": "U06868T6ADV",
"type": "message",
"ts": "1712878479.415559",
"client_msg_id": "fce57841-0446-4720-aa1d-557e162c7667",
"text": "BOTH OF YOU GET IN HERE HEEEHEEEHEEE",
"team": "T9RQLQ0KV",
"blocks": [
{
"type": "rich_text",
"block_id": "6G5BZ",
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "BOTH OF YOU GET IN HERE HEEEHEEEHEEE"
}
]
}
]
}
],
"channel": "C06U1DDBBU4",
"event_ts": "1712878479.415559",
"channel_type": "channel"
},
"type": "event_callback",
"event_id": "Ev06U1F01XMJ",
"event_time": 1712878479,
"authorizations": [
{
"enterprise_id": null,
"team_id": "T9RQLQ0KV",
"user_id": "U06TS9M7ABG",
"is_bot": true,
"is_enterprise_install": false
}
],
"is_ext_shared_channel": false,
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUOVJRTFEwS1YiLCJhaWQiOiJBMDZUWUg3Q0FMQiIsImNpZCI6IkMwNlUxRERCQlU0In0"
}

View File

@@ -0,0 +1,52 @@
{
"hint": "a thread message",
"token": "redacted",
"team_id": "T9RQLQ0KV",
"context_team_id": "T9RQLQ0KV",
"context_enterprise_id": null,
"api_app_id": "A06TYH7CALB",
"event": {
"user": "U06868T6ADV",
"type": "message",
"ts": "1712878566.650149",
"client_msg_id": "5d9d586b-eee0-40f8-b15e-66245c073a4c",
"text": "in a thread",
"team": "T9RQLQ0KV",
"thread_ts": "1712877772.926539",
"parent_user_id": "U06868T6ADV",
"blocks": [
{
"type": "rich_text",
"block_id": "KHSCu",
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "in a thread"
}
]
}
]
}
],
"channel": "C06U1DDBBU4",
"event_ts": "1712878566.650149",
"channel_type": "channel"
},
"type": "event_callback",
"event_id": "Ev06TW3WE58V",
"event_time": 1712878566,
"authorizations": [
{
"enterprise_id": null,
"team_id": "T9RQLQ0KV",
"user_id": "U06TS9M7ABG",
"is_bot": true,
"is_enterprise_install": false
}
],
"is_ext_shared_channel": false,
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUOVJRTFEwS1YiLCJhaWQiOiJBMDZUWUg3Q0FMQiIsImNpZCI6IkMwNlUxRERCQlU0In0"
}

View File

@@ -0,0 +1,52 @@
{
"hint": "a thread reply to an alert",
"token": "redacted",
"team_id": "T9RQLQ0KV",
"context_team_id": "T9RQLQ0KV",
"context_enterprise_id": null,
"api_app_id": "A06TYH7CALB",
"event": {
"user": "U06868T6ADV",
"type": "message",
"ts": "1712930706.598629",
"client_msg_id": "880fc69f-de95-4c1e-90a7-aae701a40c21",
"text": "I gotta do this",
"team": "T9RQLQ0KV",
"thread_ts": "1712927439.728409",
"parent_user_id": "U03RUK7FBUY",
"blocks": [
{
"type": "rich_text",
"block_id": "Cp0IU",
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "I gotta do this"
}
]
}
]
}
],
"channel": "C06U1DDBBU4",
"event_ts": "1712930706.598629",
"channel_type": "channel"
},
"type": "event_callback",
"event_id": "Ev06UF3U1LM7",
"event_time": 1712930706,
"authorizations": [
{
"enterprise_id": null,
"team_id": "T9RQLQ0KV",
"user_id": "U06TS9M7ABG",
"is_bot": true,
"is_enterprise_install": false
}
],
"is_ext_shared_channel": false,
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUOVJRTFEwS1YiLCJhaWQiOiJBMDZUWUg3Q0FMQiIsImNpZCI6IkMwNlUxRERCQlU0In0"
}

View File

@@ -0,0 +1,127 @@
{
"hint": "a new alert from opsgenie",
"token": "redacted",
"team_id": "T9RQLQ0KV",
"context_team_id": "T9RQLQ0KV",
"context_enterprise_id": null,
"api_app_id": "A06TYH7CALB",
"event": {
"user": "U03RUK7FBUY",
"type": "message",
"ts": "1712927439.728409",
"bot_id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"text": "",
"team": "T9RQLQ0KV",
"bot_profile": {
"id": "B03RHGBPH2M",
"deleted": false,
"name": "Opsgenie for Alert Management",
"updated": 1658887059,
"app_id": "A286WATV2",
"icons": {
"image_36": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_36.png",
"image_48": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_48.png",
"image_72": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_72.png"
},
"team_id": "T9RQLQ0KV"
},
"attachments": [
{
"id": 1,
"color": "F4511E",
"fallback": "New alert: \"[Grafana]: Firing: Alertconfig Workflow Failed\" <https://opsg.in/a/i/render/38152bc5-bc5d-411d-9feb-d285af5b6481-1712927439305|11071>\nTags: alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"text": "At least one alertconfig run has failed unexpectedly.\nDashboard: <https://grafana.render.com/d/VLZU83YVk?orgId=1>\nPanel: <https://grafana.render.com/d/VLZU83YVk?orgId=1&amp;viewPanel=17>\nSource: <https://grafana.render.com/alerting/grafana/fa7b06b8-b4d8-4979-bce7-5e1c432edd81/view?orgId=1>",
"title": "#11071: [Grafana]: Firing: Alertconfig Workflow Failed",
"title_link": "https://opsg.in/a/i/render/38152bc5-bc5d-411d-9feb-d285af5b6481-1712927439305",
"author_name": "New Alert created via Grafana - Datastore Slack",
"callback_id": "bbd4a269-08a9-470e-ba79-ce238ac03dc7_05fa2e9b-bec4-4a7e-842d-36043d267a13_11071",
"fields": [
{
"value": "P3",
"title": "Priority",
"short": true
},
{
"value": "alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"title": "Tags",
"short": true
},
{
"value": "Datastores Non-Critical",
"title": "Routed Teams",
"short": true
}
],
"mrkdwn_in": [
"pretext",
"text"
],
"actions": [
{
"id": "1",
"name": "acknowledge",
"text": "Acknowledge",
"type": "button",
"value": "ack",
"style": "default"
},
{
"id": "2",
"name": "close",
"text": "Close",
"type": "button",
"value": "close",
"style": "primary"
},
{
"id": "3",
"name": "action",
"text": "Other actions...",
"type": "select",
"data_source": "static",
"options": [
{
"text": "Assign",
"value": "assign"
},
{
"text": "Take Ownership",
"value": "own"
},
{
"text": "Snooze",
"value": "snooze"
},
{
"text": "Add Note",
"value": "addNote"
},
{
"text": "Update Priority",
"value": "updatePriority"
}
]
}
]
}
],
"channel": "C06U1DDBBU4",
"event_ts": "1712927439.728409",
"channel_type": "channel"
},
"type": "event_callback",
"event_id": "Ev06UEPF5BSM",
"event_time": 1712927439,
"authorizations": [
{
"enterprise_id": null,
"team_id": "T9RQLQ0KV",
"user_id": "U06TS9M7ABG",
"is_bot": true,
"is_enterprise_install": false
}
],
"is_ext_shared_channel": false,
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUOVJRTFEwS1YiLCJhaWQiOiJBMDZUWUg3Q0FMQiIsImNpZCI6IkMwNlUxRERCQlU0In0"
}

View File

@@ -0,0 +1,192 @@
{
"hint": "a new alert from opsgenie",
"token": "redacted",
"team_id": "T9RQLQ0KV",
"context_team_id": "T9RQLQ0KV",
"context_enterprise_id": null,
"api_app_id": "A06TYH7CALB",
"event": {
"type": "message",
"subtype": "message_changed",
"message": {
"user": "U03RUK7FBUY",
"type": "message",
"edited": {
"user": "B03RHGBPH2M",
"ts": "1712925631.000000"
},
"bot_id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"text": "",
"team": "T9RQLQ0KV",
"bot_profile": {
"id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"name": "Opsgenie for Alert Management",
"icons": {
"image_36": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_36.png",
"image_48": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_48.png",
"image_72": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_72.png"
},
"deleted": false,
"updated": 1658887059,
"team_id": "T9RQLQ0KV"
},
"attachments": [
{
"id": 1,
"color": "2ecc71",
"fallback": "\"[Grafana]: Firing: Alertconfig Workflow Failed\" <https://opsg.in/a/i/render/ec6e7c4b-90a6-4c3c-94e9-901f6b7ada47-1712922031403|11070>\nTags: alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"text": "At least one alertconfig run has failed unexpectedly.\nDashboard: <https://grafana.render.com/d/VLZU83YVk?orgId=1>\nPanel: <https://grafana.render.com/d/VLZU83YVk?orgId=1&amp;viewPanel=17>\nSource: <https://grafana.render.com/alerting/grafana/fa7b06b8-b4d8-4979-bce7-5e1c432edd81/view?orgId=1>",
"title": "#11070: [Grafana]: Firing: Alertconfig Workflow Failed",
"title_link": "https://opsg.in/a/i/render/ec6e7c4b-90a6-4c3c-94e9-901f6b7ada47-1712922031403",
"callback_id": "bbd4a269-08a9-470e-ba79-ce238ac03dc7_05fa2e9b-bec4-4a7e-842d-36043d267a13_11070",
"fields": [
{
"value": "P3",
"title": "Priority",
"short": true
},
{
"value": "alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"title": "Tags",
"short": true
},
{
"value": "Datastores Non-Critical",
"title": "Routed Teams",
"short": true
}
],
"mrkdwn_in": [
"text"
]
}
],
"ts": "1712922031.821339",
"source_team": "T9RQLQ0KV",
"user_team": "T9RQLQ0KV"
},
"previous_message": {
"user": "U03RUK7FBUY",
"type": "message",
"ts": "1712922031.821339",
"bot_id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"text": "",
"team": "T9RQLQ0KV",
"bot_profile": {
"id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"name": "Opsgenie for Alert Management",
"icons": {
"image_36": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_36.png",
"image_48": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_48.png",
"image_72": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_72.png"
},
"deleted": false,
"updated": 1658887059,
"team_id": "T9RQLQ0KV"
},
"attachments": [
{
"id": 1,
"color": "F4511E",
"fallback": "New alert: \"[Grafana]: Firing: Alertconfig Workflow Failed\" <https://opsg.in/a/i/render/ec6e7c4b-90a6-4c3c-94e9-901f6b7ada47-1712922031403|11070>\nTags: alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"text": "At least one alertconfig run has failed unexpectedly.\nDashboard: <https://grafana.render.com/d/VLZU83YVk?orgId=1>\nPanel: <https://grafana.render.com/d/VLZU83YVk?orgId=1&amp;viewPanel=17>\nSource: <https://grafana.render.com/alerting/grafana/fa7b06b8-b4d8-4979-bce7-5e1c432edd81/view?orgId=1>",
"title": "#11070: [Grafana]: Firing: Alertconfig Workflow Failed",
"title_link": "https://opsg.in/a/i/render/ec6e7c4b-90a6-4c3c-94e9-901f6b7ada47-1712922031403",
"author_name": "New Alert created via Grafana - Datastore Slack",
"callback_id": "bbd4a269-08a9-470e-ba79-ce238ac03dc7_05fa2e9b-bec4-4a7e-842d-36043d267a13_11070",
"fields": [
{
"value": "P3",
"title": "Priority",
"short": true
},
{
"value": "alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"title": "Tags",
"short": true
},
{
"value": "Datastores Non-Critical",
"title": "Routed Teams",
"short": true
}
],
"mrkdwn_in": [
"pretext",
"text"
],
"actions": [
{
"id": "1",
"name": "acknowledge",
"text": "Acknowledge",
"type": "button",
"value": "ack",
"style": "default"
},
{
"id": "2",
"name": "close",
"text": "Close",
"type": "button",
"value": "close",
"style": "primary"
},
{
"id": "3",
"name": "action",
"text": "Other actions...",
"type": "select",
"data_source": "static",
"options": [
{
"text": "Assign",
"value": "assign"
},
{
"text": "Take Ownership",
"value": "own"
},
{
"text": "Snooze",
"value": "snooze"
},
{
"text": "Add Note",
"value": "addNote"
},
{
"text": "Update Priority",
"value": "updatePriority"
}
]
}
]
}
]
},
"channel": "C06U1DDBBU4",
"hidden": true,
"ts": "1712925631.000400",
"event_ts": "1712925631.000400",
"channel_type": "channel"
},
"type": "event_callback",
"event_id": "Ev06UQNCJKNC",
"event_time": 1712925631,
"authorizations": [
{
"enterprise_id": null,
"team_id": "T9RQLQ0KV",
"user_id": "U06TS9M7ABG",
"is_bot": true,
"is_enterprise_install": false
}
],
"is_ext_shared_channel": false,
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUOVJRTFEwS1YiLCJhaWQiOiJBMDZUWUg3Q0FMQiIsImNpZCI6IkMwNlUxRERCQlU0In0"
}

View File

@@ -0,0 +1,127 @@
{
"hint": "an alert firing that has a render_id",
"token": "redacted",
"team_id": "T9RQLQ0KV",
"context_team_id": "T9RQLQ0KV",
"context_enterprise_id": null,
"api_app_id": "A06TYH7CALB",
"event": {
"user": "U03RUK7FBUY",
"type": "message",
"ts": "1712911957.023359",
"bot_id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"text": "",
"team": "T9RQLQ0KV",
"bot_profile": {
"id": "B03RHGBPH2M",
"deleted": false,
"name": "Opsgenie for Alert Management",
"updated": 1658887059,
"app_id": "A286WATV2",
"icons": {
"image_36": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_36.png",
"image_48": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_48.png",
"image_72": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_72.png"
},
"team_id": "T9RQLQ0KV"
},
"attachments": [
{
"id": 1,
"color": "F4511E",
"fallback": "New alert: \"[Grafana]: Firing: [Oregon-1] Wal Receive Count Alert\" <https://opsg.in/a/i/render/c1565736-aac4-4a78-b7db-65e4d90a59f2-1712911956739|11067>\nTags: alertname:[oregon-1] WAL Receive Count alert, business_hours:true, cluster:oregon-1, contacts:\"OpsGenie - Datastore\",\"Slack - Datastore, grafana_folder:[Generated] oregon-1, template-source:ab1da08c-129f-4f08-b505-db44929eb8",
"text": "Replica for dpg-cn7te6nsc6pc73ak5pig-b cannot start streaming replication, because the primary has already deleted the needed WAL segment.\n\nSync the replica from scratch using the runbook.\nRunbook: <https://slab.render.com/posts/runbook-postgres-replica-too-far-behind-pr084dj4>\nSource: <https://grafana.render.com/alerting/grafana/aecdfbcf-5298-4679-bd20-d9dec32ee2a0/view?orgId=1>",
"title": "#11067: [Grafana]: Firing: [Oregon-1] Wal Receive Count Alert",
"title_link": "https://opsg.in/a/i/render/c1565736-aac4-4a78-b7db-65e4d90a59f2-1712911956739",
"author_name": "New Alert created via Grafana - Datastore Slack",
"callback_id": "bbd4a269-08a9-470e-ba79-ce238ac03dc7_05fa2e9b-bec4-4a7e-842d-36043d267a13_11067",
"fields": [
{
"value": "P3",
"title": "Priority",
"short": true
},
{
"value": "alertname:[oregon-1] WAL Receive Count alert, business_hours:true, cluster:oregon-1, contacts:\"OpsGenie - Datastore\",\"Slack - Datastore, grafana_folder:[Generated] oregon-1, template-source:ab1da08c-129f-4f08-b505-db44929eb8",
"title": "Tags",
"short": true
},
{
"value": "Datastores Non-Critical",
"title": "Routed Teams",
"short": true
}
],
"mrkdwn_in": [
"pretext",
"text"
],
"actions": [
{
"id": "1",
"name": "acknowledge",
"text": "Acknowledge",
"type": "button",
"value": "ack",
"style": "default"
},
{
"id": "2",
"name": "close",
"text": "Close",
"type": "button",
"value": "close",
"style": "primary"
},
{
"id": "3",
"name": "action",
"text": "Other actions...",
"type": "select",
"data_source": "static",
"options": [
{
"text": "Assign",
"value": "assign"
},
{
"text": "Take Ownership",
"value": "own"
},
{
"text": "Snooze",
"value": "snooze"
},
{
"text": "Add Note",
"value": "addNote"
},
{
"text": "Update Priority",
"value": "updatePriority"
}
]
}
]
}
],
"channel": "C06U1DDBBU4",
"event_ts": "1712911957.023359",
"channel_type": "channel"
},
"type": "event_callback",
"event_id": "Ev06U0TNBXV0",
"event_time": 1712911957,
"authorizations": [
{
"enterprise_id": null,
"team_id": "T9RQLQ0KV",
"user_id": "U06TS9M7ABG",
"is_bot": true,
"is_enterprise_install": false
}
],
"is_ext_shared_channel": false,
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUOVJRTFEwS1YiLCJhaWQiOiJBMDZUWUg3Q0FMQiIsImNpZCI6IkMwNlUxRERCQlU0In0"
}

View File

@@ -0,0 +1,192 @@
{
"hint": "an edit to resolve an alert from opsgenie",
"token": "redacted",
"team_id": "T9RQLQ0KV",
"context_team_id": "T9RQLQ0KV",
"context_enterprise_id": null,
"api_app_id": "A06TYH7CALB",
"event": {
"type": "message",
"subtype": "message_changed",
"message": {
"user": "U03RUK7FBUY",
"type": "message",
"edited": {
"user": "B03RHGBPH2M",
"ts": "1712916339.000000"
},
"bot_id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"text": "",
"team": "T9RQLQ0KV",
"bot_profile": {
"id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"name": "Opsgenie for Alert Management",
"icons": {
"image_36": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_36.png",
"image_48": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_48.png",
"image_72": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_72.png"
},
"deleted": false,
"updated": 1658887059,
"team_id": "T9RQLQ0KV"
},
"attachments": [
{
"id": 1,
"color": "2ecc71",
"fallback": "\"[Grafana]: Firing: Alertconfig Workflow Failed\" <https://opsg.in/a/i/render/ba6f8300-b869-4aba-9d02-c142237ae59e-1712912739431|11069>\nTags: alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"text": "At least one alertconfig run has failed unexpectedly.\nDashboard: <https://grafana.render.com/d/VLZU83YVk?orgId=1>\nPanel: <https://grafana.render.com/d/VLZU83YVk?orgId=1&amp;viewPanel=17>\nSource: <https://grafana.render.com/alerting/grafana/fa7b06b8-b4d8-4979-bce7-5e1c432edd81/view?orgId=1>",
"title": "#11069: [Grafana]: Firing: Alertconfig Workflow Failed",
"title_link": "https://opsg.in/a/i/render/ba6f8300-b869-4aba-9d02-c142237ae59e-1712912739431",
"callback_id": "bbd4a269-08a9-470e-ba79-ce238ac03dc7_05fa2e9b-bec4-4a7e-842d-36043d267a13_11069",
"fields": [
{
"value": "P3",
"title": "Priority",
"short": true
},
{
"value": "alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"title": "Tags",
"short": true
},
{
"value": "Datastores Non-Critical",
"title": "Routed Teams",
"short": true
}
],
"mrkdwn_in": [
"text"
]
}
],
"ts": "1712912739.723049",
"source_team": "T9RQLQ0KV",
"user_team": "T9RQLQ0KV"
},
"previous_message": {
"user": "U03RUK7FBUY",
"type": "message",
"ts": "1712912739.723049",
"bot_id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"text": "",
"team": "T9RQLQ0KV",
"bot_profile": {
"id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"name": "Opsgenie for Alert Management",
"icons": {
"image_36": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_36.png",
"image_48": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_48.png",
"image_72": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_72.png"
},
"deleted": false,
"updated": 1658887059,
"team_id": "T9RQLQ0KV"
},
"attachments": [
{
"id": 1,
"color": "F4511E",
"fallback": "New alert: \"[Grafana]: Firing: Alertconfig Workflow Failed\" <https://opsg.in/a/i/render/ba6f8300-b869-4aba-9d02-c142237ae59e-1712912739431|11069>\nTags: alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"text": "At least one alertconfig run has failed unexpectedly.\nDashboard: <https://grafana.render.com/d/VLZU83YVk?orgId=1>\nPanel: <https://grafana.render.com/d/VLZU83YVk?orgId=1&amp;viewPanel=17>\nSource: <https://grafana.render.com/alerting/grafana/fa7b06b8-b4d8-4979-bce7-5e1c432edd81/view?orgId=1>",
"title": "#11069: [Grafana]: Firing: Alertconfig Workflow Failed",
"title_link": "https://opsg.in/a/i/render/ba6f8300-b869-4aba-9d02-c142237ae59e-1712912739431",
"author_name": "New Alert created via Grafana - Datastore Slack",
"callback_id": "bbd4a269-08a9-470e-ba79-ce238ac03dc7_05fa2e9b-bec4-4a7e-842d-36043d267a13_11069",
"fields": [
{
"value": "P3",
"title": "Priority",
"short": true
},
{
"value": "alertname:Alertconfig Workflow Failed, grafana_folder:Datastores, rule_uid:a7639f7e-6950-41be-850a-b22119f74cbb",
"title": "Tags",
"short": true
},
{
"value": "Datastores Non-Critical",
"title": "Routed Teams",
"short": true
}
],
"mrkdwn_in": [
"pretext",
"text"
],
"actions": [
{
"id": "1",
"name": "acknowledge",
"text": "Acknowledge",
"type": "button",
"value": "ack",
"style": "default"
},
{
"id": "2",
"name": "close",
"text": "Close",
"type": "button",
"value": "close",
"style": "primary"
},
{
"id": "3",
"name": "action",
"text": "Other actions...",
"type": "select",
"data_source": "static",
"options": [
{
"text": "Assign",
"value": "assign"
},
{
"text": "Take Ownership",
"value": "own"
},
{
"text": "Snooze",
"value": "snooze"
},
{
"text": "Add Note",
"value": "addNote"
},
{
"text": "Update Priority",
"value": "updatePriority"
}
]
}
]
}
]
},
"channel": "C06U1DDBBU4",
"hidden": true,
"ts": "1712916339.000300",
"event_ts": "1712916339.000300",
"channel_type": "channel"
},
"type": "event_callback",
"event_id": "Ev06TLMY8FJB",
"event_time": 1712916339,
"authorizations": [
{
"enterprise_id": null,
"team_id": "T9RQLQ0KV",
"user_id": "U06TS9M7ABG",
"is_bot": true,
"is_enterprise_install": false
}
],
"is_ext_shared_channel": false,
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUOVJRTFEwS1YiLCJhaWQiOiJBMDZUWUg3Q0FMQiIsImNpZCI6IkMwNlUxRERCQlU0In0"
}

View File

@@ -0,0 +1,55 @@
{
"hint": "a teeny closed alert message",
"token": "redacted",
"team_id": "T9RQLQ0KV",
"context_team_id": "T9RQLQ0KV",
"context_enterprise_id": null,
"api_app_id": "A06TYH7CALB",
"event": {
"user": "U03RUK7FBUY",
"type": "message",
"ts": "1712925631.682559",
"bot_id": "B03RHGBPH2M",
"app_id": "A286WATV2",
"text": "",
"team": "T9RQLQ0KV",
"bot_profile": {
"id": "B03RHGBPH2M",
"deleted": false,
"name": "Opsgenie for Alert Management",
"updated": 1658887059,
"app_id": "A286WATV2",
"icons": {
"image_36": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_36.png",
"image_48": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_48.png",
"image_72": "https://avatars.slack-edge.com/2019-05-30/652285939191_7831939cc30ef7159561_72.png"
},
"team_id": "T9RQLQ0KV"
},
"attachments": [
{
"id": 1,
"color": "2ecc71",
"fallback": "Alert API closed alert <https://opsg.in/a/i/render/ec6e7c4b-90a6-4c3c-94e9-901f6b7ada47-1712922031403|#11070> \"[Grafana]: Firing: Alertconfig Workflow Failed\"",
"text": "Alert API closed alert <https://opsg.in/a/i/render/ec6e7c4b-90a6-4c3c-94e9-901f6b7ada47-1712922031403|#11070> \"[Grafana]: Firing: Alertconfig Workflow Failed\""
}
],
"channel": "C06U1DDBBU4",
"event_ts": "1712925631.682559",
"channel_type": "channel"
},
"type": "event_callback",
"event_id": "Ev06U1R10TPV",
"event_time": 1712925631,
"authorizations": [
{
"enterprise_id": null,
"team_id": "T9RQLQ0KV",
"user_id": "U06TS9M7ABG",
"is_bot": true,
"is_enterprise_install": false
}
],
"is_ext_shared_channel": false,
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUOVJRTFEwS1YiLCJhaWQiOiJBMDZUWUg3Q0FMQiIsImNpZCI6IkMwNlUxRERCQlU0In0"
}

View File

@@ -1,3 +0,0 @@
package main
type Writer struct{}