Skip to content

Commit

Permalink
feat: add discord reporter
Browse files Browse the repository at this point in the history
  • Loading branch information
freak12techno committed May 19, 2024
1 parent 5cb4556 commit cb4ded3
Show file tree
Hide file tree
Showing 16 changed files with 432 additions and 0 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ toolchain go1.22.1
require (
cosmossdk.io/math v1.0.1
github.com/BurntSushi/toml v1.1.0
github.com/bwmarrin/discordgo v0.28.1
github.com/creasty/defaults v1.7.0
github.com/guregu/null/v5 v5.0.0
github.com/robfig/cron/v3 v3.0.1
Expand All @@ -27,13 +28,15 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 // indirect
go.opentelemetry.io/otel/metric v1.26.0 // indirect
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.19.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ cosmossdk.io/math v1.0.1 h1:Qx3ifyOPaMLNH/89WeZFH268yCvU4xEcnPLu3sJqPPg=
cosmossdk.io/math v1.0.1/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
Expand All @@ -28,6 +30,8 @@ github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3K
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/guregu/null/v5 v5.0.0 h1:PRxjqyOekS11W+w/7Vfz6jgJE/BCwELWtgvOJzddimw=
Expand Down Expand Up @@ -86,7 +90,10 @@ go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naR
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
Expand Down
9 changes: 9 additions & 0 deletions pkg/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
mutes "main/pkg/mutes"
"main/pkg/report"
reportersPkg "main/pkg/reporters"
"main/pkg/reporters/discord"
"main/pkg/reporters/pagerduty"
"main/pkg/reporters/telegram"
"main/pkg/state"
Expand Down Expand Up @@ -73,6 +74,14 @@ func NewApp(configPath string, filesystem fs.FS, version string) *App {
timeZone,
tracer,
),
discord.NewReporter(
config,
version,
log,
stateManager,
timeZone,
tracer,
),

Check warning on line 84 in pkg/app.go

View check run for this annotation

Codecov / codecov/patch

pkg/app.go#L77-L84

Added lines #L77 - L84 were not covered by tests
}

reportDispatcher := report.NewDispatcher(log, mutesManager, reporters, tracer)
Expand Down
202 changes: 202 additions & 0 deletions pkg/reporters/discord/discord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package discord

import (
"context"
"main/pkg/report/entry"
statePkg "main/pkg/state"
templatesPkg "main/pkg/templates"
types "main/pkg/types"
"main/pkg/utils"
"sync"
"time"

"go.opentelemetry.io/otel/trace"

"github.com/bwmarrin/discordgo"
"github.com/rs/zerolog"
)

type Reporter struct {
Token string
Guild string
Channel string

Version string

DiscordSession *discordgo.Session
Logger zerolog.Logger
Config *types.Config
Manager *statePkg.Manager
TemplatesManager templatesPkg.Manager
Commands map[string]*Command
Tracer trace.Tracer
Timezone *time.Location
}

func NewReporter(
config *types.Config,
version string,
logger *zerolog.Logger,
manager *statePkg.Manager,
timezone *time.Location,
tracer trace.Tracer,
) *Reporter {
return &Reporter{
Token: config.DiscordConfig.Token,
Guild: config.DiscordConfig.Guild,
Channel: config.DiscordConfig.Channel,
Config: config,
Logger: logger.With().Str("component", "discord_reporter").Logger(),
Manager: manager,
TemplatesManager: templatesPkg.NewDiscordTemplatesManager(logger, timezone),
Commands: make(map[string]*Command, 0),
Version: version,
Timezone: timezone,
Tracer: tracer,

Check warning on line 55 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L43-L55

Added lines #L43 - L55 were not covered by tests
}
}

func (reporter *Reporter) Init() error {
if !reporter.Enabled() {
reporter.Logger.Debug().Msg("Discord credentials not set, not creating Discord reporter")
return nil

Check warning on line 62 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L59-L62

Added lines #L59 - L62 were not covered by tests
}
session, err := discordgo.New("Bot " + reporter.Token)
if err != nil {
reporter.Logger.Warn().Err(err).Msg("Error initializing Discord bot")
return err

Check warning on line 67 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L64-L67

Added lines #L64 - L67 were not covered by tests
}

reporter.DiscordSession = session

Check warning on line 70 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L70

Added line #L70 was not covered by tests

// Open a websocket connection to Discord and begin listening.
err = session.Open()
if err != nil {
reporter.Logger.Warn().Err(err).Msg("Error opening Discord websocket session")
return nil

Check warning on line 76 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L73-L76

Added lines #L73 - L76 were not covered by tests
}

reporter.Logger.Info().Err(err).Msg("Discord bot listening")

Check warning on line 79 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L79

Added line #L79 was not covered by tests

reporter.Commands = map[string]*Command{
"help": reporter.GetHelpCommand(),

Check warning on line 82 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L81-L82

Added lines #L81 - L82 were not covered by tests
}

go reporter.InitCommands()
return nil

Check warning on line 86 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L85-L86

Added lines #L85 - L86 were not covered by tests
}

func (reporter *Reporter) InitCommands() {
session := reporter.DiscordSession
var wg sync.WaitGroup
var mutex sync.Mutex

Check warning on line 92 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L89-L92

Added lines #L89 - L92 were not covered by tests

session.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
commandName := i.ApplicationCommandData().Name

Check warning on line 95 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L94-L95

Added lines #L94 - L95 were not covered by tests

if command, ok := reporter.Commands[commandName]; ok {
command.Handler(s, i)

Check warning on line 98 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L97-L98

Added lines #L97 - L98 were not covered by tests
}
})

registeredCommands, err := session.ApplicationCommands(session.State.User.ID, reporter.Guild)
if err != nil {
reporter.Logger.Error().Err(err).Msg("Could not fetch registered commands")
return

Check warning on line 105 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L102-L105

Added lines #L102 - L105 were not covered by tests
}

for _, command := range registeredCommands {
wg.Add(1)
go func(command *discordgo.ApplicationCommand) {
defer wg.Done()

Check warning on line 111 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L108-L111

Added lines #L108 - L111 were not covered by tests

err := session.ApplicationCommandDelete(session.State.User.ID, reporter.Guild, command.ID)
if err != nil {
reporter.Logger.Error().Err(err).Str("command", command.Name).Msg("Could not delete command")
return

Check warning on line 116 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L113-L116

Added lines #L113 - L116 were not covered by tests
}
reporter.Logger.Info().Str("command", command.Name).Msg("Deleted command")

Check warning on line 118 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L118

Added line #L118 was not covered by tests
}(command)
}

wg.Wait()

Check warning on line 122 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L122

Added line #L122 was not covered by tests

for key, command := range reporter.Commands {
wg.Add(1)
go func(key string, command *Command) {
defer wg.Done()

Check warning on line 127 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L124-L127

Added lines #L124 - L127 were not covered by tests

cmd, err := session.ApplicationCommandCreate(session.State.User.ID, reporter.Guild, command.Info)
if err != nil {
reporter.Logger.Error().Err(err).Str("command", command.Info.Name).Msg("Could not create command")
return

Check warning on line 132 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L129-L132

Added lines #L129 - L132 were not covered by tests
}
reporter.Logger.Info().Str("command", cmd.Name).Msg("Created command")

Check warning on line 134 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L134

Added line #L134 was not covered by tests

mutex.Lock()
reporter.Commands[key].Info = cmd
mutex.Unlock()

Check warning on line 138 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L136-L138

Added lines #L136 - L138 were not covered by tests
}(key, command)
}

wg.Wait()

Check warning on line 142 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L142

Added line #L142 was not covered by tests
}

func (reporter *Reporter) Enabled() bool {
return reporter.Token != "" && reporter.Guild != "" && reporter.Channel != ""

Check warning on line 146 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L145-L146

Added lines #L145 - L146 were not covered by tests
}

func (reporter *Reporter) Name() string {
return "discord-reporter"

Check warning on line 150 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L149-L150

Added lines #L149 - L150 were not covered by tests
}

func (reporter *Reporter) SerializeReportEntry(e entry.ReportEntry) (string, error) {
return reporter.TemplatesManager.Render(e.Name(), e)

Check warning on line 154 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L153-L154

Added lines #L153 - L154 were not covered by tests
}

func (reporter *Reporter) SendReportEntry(reportEntry entry.ReportEntry, ctx context.Context) error {
_, span := reporter.Tracer.Start(ctx, "Sending Telegram report entry")
defer span.End()

Check warning on line 159 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L157-L159

Added lines #L157 - L159 were not covered by tests

serializedEntry, err := reporter.SerializeReportEntry(reportEntry)
if err != nil {
reporter.Logger.Err(err).Msg("Could not serialize report entry")
return err

Check warning on line 164 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L161-L164

Added lines #L161 - L164 were not covered by tests
}

_, err = reporter.DiscordSession.ChannelMessageSend(
reporter.Channel,
serializedEntry,
)

Check warning on line 170 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L167-L170

Added lines #L167 - L170 were not covered by tests

return err

Check warning on line 172 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L172

Added line #L172 was not covered by tests
}

func (reporter *Reporter) BotRespond(s *discordgo.Session, i *discordgo.InteractionCreate, text string) {
chunks := utils.SplitStringIntoChunks(text, 2000)
firstChunk, rest := chunks[0], chunks[1:]

Check warning on line 177 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L175-L177

Added lines #L175 - L177 were not covered by tests

if err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: firstChunk,
},
}); err != nil {
reporter.Logger.Error().Err(err).Msg("Error sending response")

Check warning on line 185 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L179-L185

Added lines #L179 - L185 were not covered by tests
}

for index, chunk := range rest {
if _, err := s.FollowupMessageCreate(i.Interaction, false, &discordgo.WebhookParams{
Content: chunk,
}); err != nil {
reporter.Logger.Error().
Int("chunk", index).
Err(err).
Msg("Error sending followup message")

Check warning on line 195 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L188-L195

Added lines #L188 - L195 were not covered by tests
}
}
}

func (reporter *Reporter) SerializeDate(date time.Time) string {
return date.Format(time.RFC822)

Check warning on line 201 in pkg/reporters/discord/discord.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/discord.go#L200-L201

Added lines #L200 - L201 were not covered by tests
}
26 changes: 26 additions & 0 deletions pkg/reporters/discord/help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package discord

import (
"github.com/bwmarrin/discordgo"
)

func (reporter *Reporter) GetHelpCommand() *Command {
return &Command{
Info: &discordgo.ApplicationCommand{
Name: "help",
Description: "Get the bot help",
},
Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) {
template, err := reporter.TemplatesManager.Render("help", helpRender{
Version: reporter.Version,
Commands: reporter.Commands,
})
if err != nil {
reporter.Logger.Error().Err(err).Str("template", "help").Msg("Error rendering template")
return

Check warning on line 20 in pkg/reporters/discord/help.go

View check run for this annotation

Codecov / codecov/patch

pkg/reporters/discord/help.go#L7-L20

Added lines #L7 - L20 were not covered by tests
}

reporter.BotRespond(s, i, template)
},
}
}
15 changes: 15 additions & 0 deletions pkg/reporters/discord/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package discord

import (
"github.com/bwmarrin/discordgo"
)

type Command struct {
Info *discordgo.ApplicationCommand
Handler func(s *discordgo.Session, i *discordgo.InteractionCreate)
}

type helpRender struct {
Version string
Commands map[string]*Command
}
Loading

0 comments on commit cb4ded3

Please sign in to comment.