Skip to content

Commit

Permalink
slack messaging for important events
Browse files Browse the repository at this point in the history
  • Loading branch information
gernotstarke committed Feb 2, 2024
1 parent b1ce99a commit ba7cf8c
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 1 deletion.
10 changes: 10 additions & 0 deletions go-app/cmd/slack/trySlackMessage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"arc42-status/internal/slack"
)

func main() {

slack.SendSlackMessage("trying out Slack from Golang@Hotel_Du_Train in Munich")
}
2 changes: 2 additions & 0 deletions go-app/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/klauspost/compress v1.16.3 // indirect
github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.19 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
github.com/slack-go/slack v0.12.3 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.50.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
Expand Down
7 changes: 7 additions & 0 deletions go-app/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
Expand All @@ -36,11 +37,14 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
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/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
Expand Down Expand Up @@ -78,7 +82,10 @@ github.com/shurcooL/githubv4 v0.0.0-20230704064427-599ae7bbf278 h1:kdEGVAV4sO46D
github.com/shurcooL/githubv4 v0.0.0-20230704064427-599ae7bbf278/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
github.com/slack-go/slack v0.12.3 h1:92/dfFU8Q5XP6Wp5rr5/T5JHLM5c5Smtn53fhToAP88=
github.com/slack-go/slack v0.12.3/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tursodatabase/libsql-client-go v0.0.0-20231216154754-8383a53d618f h1:teZ0Pj1Wp3Wk0JObKBiKZqgxhYwLeJhVAyj6DRgmQtY=
Expand Down
6 changes: 6 additions & 0 deletions go-app/internal/api/apiGateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"arc42-status/internal/database"
"arc42-status/internal/domain"
"arc42-status/internal/fly"
"arc42-status/internal/slack"
"embed"
"fmt"
"github.com/rs/zerolog/log"
"html/template"
"net/http"
Expand Down Expand Up @@ -72,6 +74,10 @@ func statsHTMLTableHandler(w http.ResponseWriter, r *http.Request) {
// TODO: include real IP address
database.SaveInvocationParams(r.Host, r.RequestURI)

// 4b: inform owner via Slack
msg := fmt.Sprintf("Loaded arc42 statistics in %sms on %s", domain.ArcStats.HowLongDidItTake, time.Now)

Check failure on line 78 in go-app/internal/api/apiGateway.go

View workflow job for this annotation

GitHub Actions / lint

printf: fmt.Sprintf format %s arg time.Now is a func value, not called (govet)
slack.SendSlackMessage(msg)

// 5. finally, render the template
executeTemplate(w, filepath.Join(TemplatesDir, HtmlTableTmpl), domain.ArcStats)
}
Expand Down
72 changes: 72 additions & 0 deletions go-app/internal/api/modular-stats.gohtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!-- golang template for arc42 usage statistics table -->
<!-- with addons to make it sortable with DataTables -->
<table id="sortableStatsTable" class="display">
<thead>
<tr>
<th rowspan="2"><img src="/images/minion-logo-100px.png" alt="Gopher logo"></th>
<th colspan="2" class="border-left-black text-center">7 Days</th>
<th colspan="2" class="border-left-black text-center">30 Days</th>
<th colspan="2" class="border-left-black text-center">12 Month</th>
<th colspan="2" class="border-left-black text-center">Open Tasks</th>
</tr>
<tr>
<th class="border-left-black">Visitors</th>
<th>PageViews</th>
<th class="border-left-black">Visitors</th>
<th>PageViews</th>
<th class="border-left-black">Visitors</th>
<th>PageViews</th>
<th class="border-left-black">Issues</th>
<th>Bugs</th>
</tr>
</thead>

<tbody>

{{ range .Stats4Site }}
<tr>
<td><a href="https://{{.Site }}">{{.Site}}</a></td>
<td class="border-left-black">{{ .Visitors7d}}</td>
<td>{{ .PageViews7d}}</td>
<td class="border-left-black">{{ .Visitors30d}}</td>
<td>{{ .PageViews30d}}</td>
<td class="border-left-black">{{ .Visitors12m}}</td>
<td>{{ .PageViews12m}}</td>
<td class="border-left-black">
<a href="{{.Repo}}/issues">{{.NrOfOpenIssues}}</a>
</td>
<td >
{{if (gt .NrOfOpenBugs 0)}}
<a href="{{.Repo}}/issues">{{.NrOfOpenBugs}}</a>
{{end}}
</td>
</tr>
{{ end }}
</tbody>

<tfoot> <tr>
<td class="border-top">Totals:</td>
<td class="border-top border-left-black">{{ .Totals.SumOfVisitors7d}}</td>
<td class="border-top">{{ .Totals.SumOfPageViews7d}}</td>
<td class="border-top border-left-black">{{ .Totals.SumOfVisitors30d}}</td>
<td class="border-top">{{ .Totals.SumOfPageViews30d}}</td>
<td class="border-top border-left-black">{{ .Totals.SumOfVisitors12m}}</td>
<td class="border-top">{{ .Totals.SumOfPageViews12m}}</td>
<td class="border-top border-left-black">{{ .Totals.TotalNrOfIssues}}</td>
<td class="border-top">{{ .Totals.TotalNrOfBugs}}</td>

</tr> </tfoot>
</table>


<br>
<div style="font-size: 12px; padding-bottom: 14px;">
Data collected in {{ .HowLongDidItTake }} msecs by arc42 statistics service
(v. {{.AppVersion}}) at {{.LastUpdatedString }}
running {{if .FlyRegion}}
on <a href="https://fly.io" target="_blank"> fly.io </a>
in {{.WhereDoesItRun}} (region {{.FlyRegion}})
{{ else }}
{{.WhereDoesItRun}}
{{ end}}
</div>
110 changes: 110 additions & 0 deletions go-app/internal/slack/slack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package slack

import (
"arc42-status/internal/env"
"fmt"
"github.com/rs/zerolog/log"
"github.com/slack-go/slack"
"os"
"sync"
)

const SlackTokenName = "SLACK_AUTH_TOKEN"

// SlackChannelID could be different for PROD and DEV envirionments,
// but is currently identical.
const SlackChannelID = "C06FY002V1D"

var (
once sync.Once
slackAPI *slack.Client
)

// getSlackAuthToken should not be called directly, it is only used by the Singleton GetSlack()
func getSlackAuthToken() string {
slackAuthToken := os.Getenv(SlackTokenName)
if slackAuthToken == "" {
// no value, no slack messages
// we exit here, as we have no chance of recovery
log.Error().Msgf("CRITICAL ERROR: required Slack Auth token not set.\n" +
"You need to set the 'SLACK_AUTH_TOKEN' environment variable prior to launching this application.\n")
os.Exit(14)
}
return slackAuthToken
}

// checkIfTokenValid checks if the slackAuthToken used to create slackClient
// is valid and has not been revoked
func checkIfTokenValid(api *slack.Client) error {

_, err := api.AuthTest()
if err != nil {
log.Info().Msgf("Token validation failed: %v\n", err)
return err
}
return nil
}

// getSlackAPI() returns a Slack API connection if there is a valid auth token available.
// In case of errors, it returns nil
func getSlackAPI() *slack.Client {
once.Do(func() {
var (
slackClient *slack.Client
slackError error
)

switch env.GetEnv() {
case "PROD":
{
slackClient = slack.New(getSlackAuthToken())
break
}
case "DEV", "TEST":
{
slackClient = slack.New(getSlackAuthToken(), slack.OptionDebug(true))

break
}
default:
{
// this should never happen, as env.GetEnv() needs to care for valid environments
log.Error().Msgf("Invalid environment %s specified for slack messages", env.GetEnv())
os.Exit(14)
}
}

slackError = checkIfTokenValid(slackClient)

if slackError != nil {
log.Error().Msgf("Failed to init communication with Slack: %s", slackError)
slackClient = nil
}
slackAPI = slackClient
})

return slackAPI

}

// SendSlackMessage sends the given message to the configured slack channel
// Different environments (PROD, DEV, TEST) might have different channels configured
func SendSlackMessage(msg string) {
api := getSlackAPI()

if api != nil {
channelID, timestamp, err := api.PostMessage(
SlackChannelID,
slack.MsgOptionText(msg, false),
)
if err != nil {
log.Error().Msgf("Error sending message to Slack: %s", err)
} else {

fmt.Printf("Message successfully sent to channel %s at %s", channelID, timestamp)
}
} else { // api == nil
log.Error().Msgf("Could not sent message %s to slack, invalid api token", msg)
}

}
3 changes: 2 additions & 1 deletion go-app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import (
"time"
)

const appVersion = "0.5.5"
const appVersion = "0.5.6"

// version history
// 0.5.x rate limit: limit amount of queries to external APIs
// 0.5.2: distinct env package, distinct DB for DEV, handle OPTIONS request
// 0.5.3: BUG and BUGS are both recognized
// 0.5.4: start with empty table on homepage
// 0.5.5: caching with zcache
// 0.5.6: send slack message for important system events, starting with data acquisition
// 0.4.7 replace most inline styles by css
// 0.4.6 sortable table (a: initial, b...e: fix layout issues), f: fix #94
// 0.4.5 fix missing separators in large numbers
Expand Down

0 comments on commit ba7cf8c

Please sign in to comment.