MG-853 - Add Slack output integration (#315)

* add slack integration

Signed-off-by: ianmuchyri <ianmuchiri8@gmail.com>

* allow support for multiple message options

Signed-off-by: ianmuchyri <ianmuchiri8@gmail.com>

* add message to slack struct

Signed-off-by: ianmuchyri <ianmuchiri8@gmail.com>

* update template name

Signed-off-by: ianmuchyri <ianmuchiri8@gmail.com>

* group postgres and slack

Signed-off-by: ianmuchyri <ianmuchiri8@gmail.com>

---------

Signed-off-by: ianmuchyri <ianmuchiri8@gmail.com>
This commit is contained in:
Ian Ngethe Muchiri
2025-09-26 12:24:31 +03:00
committed by GitHub
parent a4abb61239
commit 7ef90440f2
6 changed files with 81 additions and 3 deletions
+1
View File
@@ -45,6 +45,7 @@ require (
)
require (
github.com/slack-go/slack v0.17.3 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
)
+2
View File
@@ -442,6 +442,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/slack-go/slack v0.17.3 h1:zV5qO3Q+WJAQ/XwbGfNFrRMaJ5T/naqaonyPV/1TP4g=
github.com/slack-go/slack v0.17.3/go.mod h1:X+UqOufi3LYQHDnMG1vxf0J8asC6+WllXrVrhl8/Prk=
github.com/smarty/assertions v1.16.0 h1:EvHNkdRA4QHMrn75NZSoUQ/mAUXAYWfatfB01yTCzfY=
github.com/smarty/assertions v1.16.0/go.mod h1:duaaFdCS0K9dnoM50iyek/eYINOZ64gbh1Xlf6LG7AI=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
+2 -2
View File
@@ -103,14 +103,14 @@ func (re *re) handleOutput(ctx context.Context, o Runnable, r Rule, msg *messagi
case *outputs.Email:
o.Emailer = re.email
return o.Run(ctx, msg, val)
case *outputs.Postgres:
return o.Run(ctx, msg, val)
case *outputs.ChannelPublisher:
o.RePubSub = re.rePubSub
return o.Run(ctx, msg, val)
case *outputs.SenML:
o.WritersPub = re.writersPub
return o.Run(ctx, msg, val)
case *outputs.Postgres, *outputs.Slack:
return o.Run(ctx, msg, val)
default:
return fmt.Errorf("unknown output type: %T", o)
}
+3 -1
View File
@@ -26,16 +26,18 @@ const (
SaveSenMLType
EmailType
SaveRemotePgType
SlackType
)
var (
scriptKindToString = [...]string{"channels", "alarms", "save_senml", "email", "save_remote_pg"}
scriptKindToString = [...]string{"channels", "alarms", "save_senml", "email", "save_remote_pg", "slack"}
stringToScriptKind = map[string]OutputType{
"channels": ChannelsType,
"alarms": AlarmsType,
"save_senml": SaveSenMLType,
"email": EmailType,
"save_remote_pg": SaveRemotePgType,
"slack": SlackType,
}
)
+72
View File
@@ -0,0 +1,72 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package outputs
import (
"bytes"
"context"
"encoding/json"
"text/template"
"github.com/absmach/supermq/pkg/messaging"
"github.com/slack-go/slack"
)
type Slack struct {
Token string `json:"token"`
ChannelID string `json:"channel_id"`
Message string `json:"message"`
}
func (s *Slack) Run(ctx context.Context, msg *messaging.Message, val any) error {
templData := templateVal{
Message: msg,
Result: val,
}
tmpl, err := template.New("slack").Parse(s.Message)
if err != nil {
return err
}
var output bytes.Buffer
if err := tmpl.Execute(&output, templData); err != nil {
return err
}
mapping := output.String()
var message slack.Msg
if err := json.Unmarshal([]byte(mapping), &message); err != nil {
return err
}
slackClient := slack.New(s.Token)
var opts []slack.MsgOption
if message.Text != "" {
opts = append(opts, slack.MsgOptionText(message.Text, false))
}
if len(message.Attachments) > 0 {
opts = append(opts, slack.MsgOptionAttachments(message.Attachments...))
}
if len(message.Blocks.BlockSet) > 0 {
opts = append(opts, slack.MsgOptionBlocks(message.Blocks.BlockSet...))
}
_, _, err = slackClient.PostMessage(s.ChannelID, opts...)
if err != nil {
return err
}
return nil
}
func (s *Slack) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]any{
"type": SlackType.String(),
"token": s.Token,
"channel_id": s.ChannelID,
"message": s.Message,
})
}
+1
View File
@@ -50,6 +50,7 @@ var outputRegistry = map[outputs.OutputType]func() Runnable{
outputs.SaveRemotePgType: func() Runnable { return &outputs.Postgres{} },
outputs.ChannelsType: func() Runnable { return &outputs.ChannelPublisher{} },
outputs.SaveSenMLType: func() Runnable { return &outputs.SenML{} },
outputs.SlackType: func() Runnable { return &outputs.Slack{} },
}
type Rule struct {