Skip to content

Commit

Permalink
add github release discord pinging
Browse files Browse the repository at this point in the history
  • Loading branch information
topi314 committed Jun 5, 2024
1 parent a8e535a commit 3c3f135
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 18 deletions.
9 changes: 9 additions & 0 deletions config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ bot:
token: ...
guild_ids: [ ... ]

github:
server_addr: ":8080"
webhook_secret: ""
releases:
user/repo:
webhook_id: 1248018508823138376
webhook_token: "..."
ping_role: 123456789012345678

nodes:
- name: node1
address: localhost:2333
Expand Down
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/lavalink-devs/lavalink-bot
go 1.22

require (
github.com/disgoorg/disgo v0.18.5
github.com/disgoorg/disgo v0.18.7
github.com/disgoorg/disgolink/v3 v3.0.1-0.20240311001109-56f250c13235
github.com/disgoorg/json v1.1.0
github.com/disgoorg/lavalyrics-plugin v0.0.0-20240428194130-71a50d68e826
Expand All @@ -27,9 +27,9 @@ require (
github.com/magefile/mage v1.15.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.20.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
)
24 changes: 12 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZ
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disgoorg/disgo v0.18.5 h1:T4X9ARKJFwCon4xkw4Dg+SjGpFo7usQ7QCCX2+snGXQ=
github.com/disgoorg/disgo v0.18.5/go.mod h1:gkl6DBdbKUvmOOJayWPSvS52KPN/8uJGJ2f13gCEB1o=
github.com/disgoorg/disgo v0.18.7 h1:Xg5eiOdSo+wR3CDMIPh9Vmykdkwk/rdcs00vhr2U6m0=
github.com/disgoorg/disgo v0.18.7/go.mod h1:gkl6DBdbKUvmOOJayWPSvS52KPN/8uJGJ2f13gCEB1o=
github.com/disgoorg/disgolink/v3 v3.0.1-0.20240311001109-56f250c13235 h1:Mtqh7yLVBW6uKWAbrqKZv7DgXpoWBfPctok8sABYIiY=
github.com/disgoorg/disgolink/v3 v3.0.1-0.20240311001109-56f250c13235/go.mod h1:YIwjIteZcjfI7HYZWH241iRI7RjTLoN51HLDOUHVSFI=
github.com/disgoorg/json v1.1.0 h1:7xigHvomlVA9PQw9bMGO02PHGJJPqvX5AnwlYg/Tnys=
Expand Down Expand Up @@ -59,8 +59,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand All @@ -69,10 +69,10 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -86,8 +86,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
Expand All @@ -100,8 +100,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
Expand Down
2 changes: 2 additions & 0 deletions lavalinkbot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/webhook"
"github.com/disgoorg/disgolink/v3/disgolink"
"github.com/google/go-github/v52/github"
"github.com/topi314/tint"
Expand All @@ -22,6 +23,7 @@ type Bot struct {
Lavalink disgolink.Client
GitHub *github.Client
MusicQueue *PlayerManager
Webhooks map[string]webhook.Client
}

func (b *Bot) Start() error {
Expand Down
33 changes: 33 additions & 0 deletions lavalinkbot/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func ReadConfig(path string) (Config, error) {
type Config struct {
Log LogConfig `yaml:"log"`
Bot BotConfig `yaml:"bot"`
GitHub GitHubConfig `yaml:"github"`
Nodes NodeConfigs `yaml:"nodes"`
Plugins PluginConfigs `yaml:"plugins"`
}
Expand Down Expand Up @@ -76,6 +77,38 @@ func (c BotConfig) String() string {
)
}

type GitHubConfig struct {
ServerAddr string `yaml:"server_addr"`
WebhookSecret string `yaml:"webhook_secret"`
Releases map[string]GithubReleaseConfig `yaml:"github_releases"`
}

func (c GitHubConfig) String() string {
var s string
for repo, cfg := range c.Releases {
s += fmt.Sprintf("\n %s: %s", repo, cfg)
}
return fmt.Sprintf("\n ServerAddr: %s\n WebhookSecret: %s\n Releases: %s",
c.ServerAddr,
c.WebhookSecret,
s,
)
}

type GithubReleaseConfig struct {
WebhookID snowflake.ID `yaml:"webhook_id"`
WebhookToken string `yaml:"webhook_token"`
PingRole snowflake.ID `yaml:"ping_role"`
}

func (c GithubReleaseConfig) String() string {
return fmt.Sprintf("\n WebhookID: %s\n WebhookToken: %s\n PingRole: %s",
c.WebhookID,
c.WebhookToken,
c.PingRole,
)
}

type NodeConfig struct {
Name string `yaml:"name"`
Address string `yaml:"address"`
Expand Down
13 changes: 13 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"errors"
"flag"
"log/slog"
"net/http"
Expand All @@ -15,13 +16,15 @@ import (
"github.com/disgoorg/disgo/gateway"
"github.com/disgoorg/disgo/handler"
"github.com/disgoorg/disgo/handler/middleware"
"github.com/disgoorg/disgo/webhook"
"github.com/disgoorg/disgolink/v3/disgolink"
"github.com/disgoorg/sponsorblock-plugin"
"github.com/google/go-github/v52/github"
"github.com/lavalink-devs/lavalink-bot/commands"
"github.com/lavalink-devs/lavalink-bot/handlers"
"github.com/lavalink-devs/lavalink-bot/internal/maven"
"github.com/lavalink-devs/lavalink-bot/lavalinkbot"
"github.com/lavalink-devs/lavalink-bot/routes"
"github.com/mattn/go-colorable"
"github.com/topi314/tint"
)
Expand All @@ -46,6 +49,7 @@ func main() {
Timeout: 10 * time.Second,
}),
MusicQueue: lavalinkbot.NewPlayerManager(),
Webhooks: map[string]webhook.Client{},
}

cmds := &commands.Commands{Bot: b}
Expand Down Expand Up @@ -92,6 +96,15 @@ func main() {

hdlr := &handlers.Handlers{Bot: b}

mux := http.NewServeMux()
mux.Handle("POST /github/webhook", routes.HandleGithubWebhook(b))

go func() {
if err := http.ListenAndServe(cfg.GitHub.ServerAddr, mux); err != nil && !errors.Is(err, http.ErrServerClosed) {
slog.Error("failed to start github webhook server", tint.Err(err))
}
}()

if b.Client, err = disgo.New(cfg.Bot.Token,
bot.WithGatewayConfigOpts(
gateway.WithIntents(gateway.IntentGuilds, gateway.IntentGuildVoiceStates),
Expand Down
129 changes: 129 additions & 0 deletions routes/github_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package routes

import (
"errors"
"fmt"
"log/slog"
"net/http"
"regexp"
"strings"

"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/webhook"
"github.com/google/go-github/v52/github"
"github.com/lavalink-devs/lavalink-bot/lavalinkbot"
"github.com/topi314/tint"
)

var (
markdownHeaderRegex = regexp.MustCompile(`[ \t]*#+[ \t]+([^\r\n]+)`)
markdownBulletRegex = regexp.MustCompile(`([ \t]*)[*|-][ \t]+([^\r\n]+)`)
markdownCheckBoxCheckedRegex = regexp.MustCompile(`([ \t]*)[*|-][ \t]{0,4}\[x][ \t]+([^\r\n]+)`)
markdownCheckBoxUncheckedRegex = regexp.MustCompile(`([ \t]*)[*|-][ \t]{0,4}\[ ][ \t]+([^\r\n]+)`)
prURLRegex = regexp.MustCompile(`https?://github\.com/(\w+/\w+)/pull/(\d+)`)
commitURLRegex = regexp.MustCompile(`https?://github\.com/\w+/\w+/commit/([a-f\d]{7})[a-f\d]+`)
mentionRegex = regexp.MustCompile(`@(\w+)`)
)

func HandleGithubWebhook(b *lavalinkbot.Bot) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
payload, err := github.ValidatePayload(r, []byte(b.Cfg.GitHub.WebhookSecret))
if err != nil {
slog.Error("Failed to validate payload", tint.Err(err))
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
event, err := github.ParseWebHook(github.WebHookType(r), payload)
if err != nil {
slog.Error("Failed to parse webhook", tint.Err(err))
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
switch e := event.(type) {
case *github.ReleaseEvent:
err = processReleaseEvent(b, e)
}
if err != nil {
slog.Error("Failed to process webhook", tint.Err(err))
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}

func processReleaseEvent(b *lavalinkbot.Bot, e *github.ReleaseEvent) error {
if e.GetAction() != "published" {
return nil
}

repo := e.GetRepo().GetName()
fullName := e.GetRepo().GetFullName()

cfg, ok := b.Cfg.GitHub.Releases[fullName]
if !ok {
return errors.New("no config found for this repo")
}

webhookClient, ok := b.Webhooks[fullName]
if !ok {
webhookClient = webhook.New(cfg.WebhookID, cfg.WebhookToken)
b.Webhooks[fullName] = webhookClient
}

message := parseMarkdown(e.GetRelease().GetBody())
if len(message) > 1024 {
message = substr(message, 0, 1024)
if index := strings.LastIndex(message, "\n"); index != -1 {
message = message[:index]
}
message += "\n…"
}

msg, err := webhookClient.CreateMessage(discord.NewWebhookMessageCreateBuilder().
SetContent(discord.RoleMention(cfg.PingRole)).
SetEmbeds(discord.NewEmbedBuilder().
SetAuthor(
fmt.Sprintf("%s version %s has been released", repo, e.Release.GetTagName()),
e.GetRelease().GetHTMLURL(),
e.GetRepo().GetOwner().GetAvatarURL(),
).
SetDescription(message).
SetColor(0xff624a).
SetFooter("Release by "+e.GetRelease().GetAuthor().GetLogin(), e.GetRelease().GetAuthor().GetAvatarURL()).
SetTimestamp(e.GetRelease().GetCreatedAt().Time).
Build(),
).
SetAvatarURL(e.GetRepo().GetOwner().GetAvatarURL()).
Build(),
)
if err != nil {
return err
}
_, err = b.Client.Rest().CrosspostMessage(msg.ChannelID, msg.ID)
return err
}

func substr(input string, start int, length int) string {
asRunes := []rune(input)

if start >= len(asRunes) {
return ""
}

if start+length > len(asRunes) {
length = len(asRunes) - start
}

return string(asRunes[start : start+length])
}

func parseMarkdown(text string) string {
text = markdownCheckBoxCheckedRegex.ReplaceAllString(text, "$1:ballot_box_with_check: $2")
text = markdownCheckBoxUncheckedRegex.ReplaceAllString(text, "$1:white_square_button: $2")
text = markdownHeaderRegex.ReplaceAllString(text, "**$1**")
text = markdownBulletRegex.ReplaceAllString(text, "$1• $2")
text = prURLRegex.ReplaceAllString(text, "[$1#$2]($0)")
text = commitURLRegex.ReplaceAllString(text, "[`$1`]($0)")
text = mentionRegex.ReplaceAllString(text, "[@$1](https://github.com/$1)")
return text
}

0 comments on commit 3c3f135

Please sign in to comment.