Skip to content

Commit

Permalink
Discord threads (projectdiscovery#191)
Browse files Browse the repository at this point in the history
* Fix a spelling mistake in README.md

change example `notify -version` to `notify -verbose`. It's a small typo.

* add yaml parameters to discord config

* fix Username and avatarURL passing to shoutrrr service

* add SendThreaded function with corresponding types which sends messages to specific chat threads. Implement optional threads setting for discord provider.

* small refactor

* removing extra new line

---------

Co-authored-by: 程越 <44216455+cyicz123@users.noreply.github.com>
Co-authored-by: Sandeep Singh <sandeep@projectdiscovery.io>
Co-authored-by: mzack <marco.rivoli.nvh@gmail.com>
  • Loading branch information
4 people authored Mar 9, 2023
1 parent ab77820 commit e0dcf6f
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 24 deletions.
8 changes: 7 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ updates:
commit-message:
prefix: "chore"
include: "scope"
labels:
- "Type: Maintenance"

# Maintain dependencies for go modules
- package-ecosystem: "gomod"
Expand All @@ -25,6 +27,8 @@ updates:
commit-message:
prefix: "chore"
include: "scope"
labels:
- "Type: Maintenance"

# Maintain dependencies for docker
- package-ecosystem: "docker"
Expand All @@ -34,4 +38,6 @@ updates:
target-branch: "dev"
commit-message:
prefix: "chore"
include: "scope"
include: "scope"
labels:
- "Type: Maintenance"
1 change: 0 additions & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ on:
pull_request:
workflow_dispatch:


jobs:
build:
name: Test Builds
Expand Down
4 changes: 2 additions & 2 deletions internal/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package runner
import (
"bufio"
"crypto/tls"
"io/ioutil"
"io"
"log"
"net/http"
"net/url"
Expand Down Expand Up @@ -51,7 +51,7 @@ func NewRunner(options *types.Options) (*Runner, error) {
}

// Discard all internal logs
shoutrrr.SetLogger(log.New(ioutil.Discard, "", 0))
shoutrrr.SetLogger(log.New(io.Discard, "", 0))

prClient, err := providers.New(&providerOptions, options)
if err != nil {
Expand Down
56 changes: 36 additions & 20 deletions pkg/providers/discord/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package discord

import (
"fmt"
"net/url"

"github.com/containrrr/shoutrrr"
"github.com/oriser/regroup"
Expand All @@ -14,6 +13,8 @@ import (
sliceutil "github.com/projectdiscovery/utils/slice"
)

var reDiscordWebhook = regroup.MustCompile(`(?P<scheme>https?):\/\/(?P<domain>(?:ptb\.|canary\.)?discord(?:app)?\.com)\/api(?:\/)?(?P<api_version>v\d{1,2})?\/webhooks\/(?P<webhook_identifier>\d{17,19})\/(?P<webhook_token>[\w\-]{68})`)

type Provider struct {
Discord []*Options `yaml:"discord,omitempty"`
counter int
Expand All @@ -24,6 +25,8 @@ type Options struct {
DiscordWebHookURL string `yaml:"discord_webhook_url,omitempty"`
DiscordWebHookUsername string `yaml:"discord_username,omitempty"`
DiscordWebHookAvatarURL string `yaml:"discord_avatar,omitempty"`
DiscordThreads bool `yaml:"discord_threads,omitempty"`
DiscordThreadID string `yaml:"discord_thread_id,omitempty"`
DiscordFormat string `yaml:"discord_format,omitempty"`
}

Expand All @@ -41,31 +44,44 @@ func New(options []*Options, ids []string) (*Provider, error) {
return provider, nil
}
func (p *Provider) Send(message, CliFormat string) error {
var DiscordErr error
p.counter++
var errs []error
for _, pr := range p.Discord {
msg := utils.FormatMessage(message, utils.SelectFormat(CliFormat, pr.DiscordFormat), p.counter)

discordWebhookRegex := regroup.MustCompile(`(?P<scheme>https?):\/\/(?P<domain>(?:ptb\.|canary\.)?discord(?:app)?\.com)\/api(?:\/)?(?P<api_version>v\d{1,2})?\/webhooks\/(?P<webhook_identifier>\d{17,19})\/(?P<webhook_token>[\w\-]{68})`)
matchedGroups, err := discordWebhookRegex.Groups(pr.DiscordWebHookURL)
if pr.DiscordThreads {
if pr.DiscordThreadID == "" {
err := fmt.Errorf("thread_id value is required when discord_threads is set to true. check your configuration at id: %s", pr.ID)
errs = append(errs, err)
continue
}
if err := pr.SendThreaded(msg); err != nil {
err = errors.Wrapf(err, "failed to send discord notification for id: %s ", pr.ID)
errs = append(errs, err)
continue
}

if err != nil {
err := fmt.Errorf("incorrect discord configuration for id: %s ", pr.ID)
DiscordErr = multierr.Append(DiscordErr, err)
continue
}
} else {
matchedGroups, err := reDiscordWebhook.Groups(pr.DiscordWebHookURL)
if err != nil {
err := fmt.Errorf("incorrect discord configuration for id: %s ", pr.ID)
errs = append(errs, err)
continue
}

webhookID, webhookToken := matchedGroups["webhook_identifier"], matchedGroups["webhook_token"]
url := fmt.Sprintf("discord://%s@%s?splitlines=no&username=%s", webhookToken, webhookID,
url.QueryEscape(pr.DiscordWebHookUsername))
webhookID, webhookToken := matchedGroups["webhook_identifier"], matchedGroups["webhook_token"]

sendErr := shoutrrr.Send(url, msg)
if sendErr != nil {
sendErr = errors.Wrap(sendErr, fmt.Sprintf("failed to send discord notification for id: %s ", pr.ID))
DiscordErr = multierr.Append(DiscordErr, sendErr)
continue
//Reference: https://containrrr.dev/shoutrrr/v0.6/getting-started/
url := fmt.Sprintf("discord://%s@%s?username=%s&avatarurl=%s&splitlines=no",
webhookToken,
webhookID,
pr.DiscordWebHookUsername,
pr.DiscordWebHookAvatarURL)
if err := shoutrrr.Send(url, msg); err != nil {
errs = append(errs, errors.Wrapf(err, "failed to send discord notification for id: %s ", pr.ID))
}
}

gologger.Verbose().Msgf("discord notification sent for id: %s", pr.ID)
}
return DiscordErr
}
return multierr.Combine(errs...)
}
41 changes: 41 additions & 0 deletions pkg/providers/discord/discord_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package discord

import (
"bytes"
"encoding/json"
"fmt"
"net/http"

"github.com/projectdiscovery/notify/pkg/utils/httpreq"
)

func (options *Options) SendThreaded(message string) error {

payload := APIRequest{
Content: message,
Username: options.DiscordWebHookUsername,
AvatarURL: options.DiscordWebHookAvatarURL,
}

encoded, err := json.Marshal(payload)
if err != nil {
return err
}

body := bytes.NewReader(encoded)

webHookURL := fmt.Sprintf("%s?thread_id=%s", options.DiscordWebHookURL, options.DiscordThreadID)

req, err := http.NewRequest(http.MethodPost, webHookURL, body)
req.Header.Set("Content-Type", "application/json")
if err != nil {
return err
}

_, err = httpreq.NewClient().Do(req)
if err != nil {
return err
}

return nil
}
7 changes: 7 additions & 0 deletions pkg/providers/discord/discord_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package discord

type APIRequest struct {
Content string `json:"content,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
Username string `json:"username,omitempty"`
}

0 comments on commit e0dcf6f

Please sign in to comment.