diff --git a/.github/workflows/dockerhub-push.yml b/.github/workflows/dockerhub-push.yml index b0d4933..a257a54 100644 --- a/.github/workflows/dockerhub-push.yml +++ b/.github/workflows/dockerhub-push.yml @@ -1,34 +1,39 @@ name: 🌥 Docker Push on: - release: - types: [published] + workflow_run: + workflows: ["🎉 Release Binary"] + types: + - completed workflow_dispatch: jobs: docker: runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Git Checkout uses: actions/checkout@v2 - - - name: Set up QEMU + + - name: Get Github tag + id: meta + run: | + echo "::set-output name=tag::$(curl --silent "https://api.github.com/repos/projectdiscovery/notify/releases/latest" | jq -r .tag_name)" + - name: Set up QEMU uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx + + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - - - name: Login to DockerHub + + - name: Login to DockerHub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - - - name: Build and push + + - name: Build and push uses: docker/build-push-action@v2 with: context: . platforms: linux/amd64,linux/arm64,linux/arm push: true - tags: projectdiscovery/notify:latest \ No newline at end of file + tags: projectdiscovery/notify:latest,projectdiscovery/notify:${{ steps.meta.outputs.tag }} \ No newline at end of file diff --git a/.github/workflows/release-binary.yml b/.github/workflows/release-binary.yml index 6fe8c82..01975e8 100644 --- a/.github/workflows/release-binary.yml +++ b/.github/workflows/release-binary.yml @@ -18,7 +18,7 @@ jobs: name: "Set up Go" uses: actions/setup-go@v2 with: - go-version: 1.16 + go-version: 1.17 - env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/Dockerfile b/Dockerfile index 17a3756..13deb78 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.16.0-alpine as build-env +FROM golang:1.17.3-alpine as build-env RUN GO111MODULE=on go get -v github.com/projectdiscovery/notify/cmd/notify FROM alpine:latest diff --git a/README.md b/README.md index a4bfa3c..6e51ae5 100644 --- a/README.md +++ b/README.md @@ -51,25 +51,26 @@ notify -h This will display help for the tool. Here are all the switches it supports. -| Flag | Description | Example | -| ---------------- | ----------------------------------------------- | --------------------------------- | -| -config | Notify configuration file | notify -config config.yaml | -| -silent | Don't print the banner | notify -silent | -| -version | Show version of notify | notify -version | -| -v | Show Verbose output | notify -v | -| -no-color | Don't Use colors in output | notify -no-color | -| -data | File path to read data from | notify -data test.txt | -| -bulk | Read and send data in bulk from file. | notify -bulk | -| -char-limit | Character limit for message (default 4000) | notify -char-limit 2000 | -| -provider-config | provider config path | notify -provider-config conf.yaml | -| -provider | provider to send the notification to (optional) | notify -provider slack,telegram | -| -id | id to send the notification to (optional) | notify -id recon,scans | +| Flag | Description | Example | +|------------------|----------------------------------------------------|----------------------------| +| -config | Notify configuration file | notify -config config.yaml | +| -silent | Don't print the banner | notify -silent | +| -version | Show version of notify | notify -version | +| -v | Show Verbose output | notify -v | +| -no-color | Don't Use colors in output | notify -nc | +| -data | File path to read data from | notify -i test.txt | +| -bulk | Read and send data in bulk from file. | notify -bulk | +| -char-limit | Character limit for message (default 4000) | notify -cl 2000 | +| -provider-config | provider config path | notify -pc provider.yaml | +| -provider | provider to send the notification to (optional) | notify -p slack,telegram | +| -id | id to send the notification to (optional) | notify -id recon,scans | +| -rate-limit | maximum number of HTTP requests to send per second | notify -rl 1 | # Notify Installation ```sh -GO111MODULE=on go get -v github.com/projectdiscovery/notify/cmd/notify +go install -v github.com/projectdiscovery/notify/cmd/notify@latest ``` ### Provider Config diff --git a/cmd/notify/notify.go b/cmd/notify/notify.go index 106c509..51d6352 100644 --- a/cmd/notify/notify.go +++ b/cmd/notify/notify.go @@ -49,19 +49,20 @@ func readConfig() { set := goflags.NewFlagSet() set.Marshal = true set.SetDescription(`Notify is a general notification tool`) - set.StringVar(&cfgFile, "config", "", "Notify configuration file") - set.BoolVar(&options.Silent, "silent", false, "Don't print the banner") - set.BoolVar(&options.Version, "version", false, "Show version of notify") - set.BoolVar(&options.Verbose, "v", false, "Show Verbose output") - set.BoolVar(&options.NoColor, "no-color", false, "Don't Use colors in output") - set.StringVar(&options.Data, "data", "", "File path to read data from") - set.BoolVar(&options.Bulk, "bulk", false, "Read the input and send it in bulk, character limit can be set using char-limit flag") - set.IntVar(&options.CharLimit, "char-limit", 4000, "Character limit for message") - set.StringVar(&options.ProviderConfig, "provider-config", "", "provider config path (default: $HOME/.config/notify/provider-config.yaml)") - set.NormalizedStringSliceVar(&options.Providers, "provider", []string{}, "provider to send the notification to (optional)") + set.StringVar(&cfgFile, "config", "", "notify configuration file") + set.StringVarP(&options.ProviderConfig, "provider-config", "pc", "", "provider config path (default: $HOME/.config/notify/provider-config.yaml)") + set.StringVarP(&options.Data, "data", "i", "", "input file to send for notify") + set.NormalizedStringSliceVarP(&options.Providers, "provider", "p", []string{}, "provider to send the notification to (optional)") set.NormalizedStringSliceVar(&options.IDs, "id", []string{}, "id to send the notification to (optional)") - set.StringVar(&options.MessageFormat, "msg-format", "{{data}}", "apply custom formatting to the text") - set.StringVar(&options.Proxy, "proxy", "", "Set http proxy to be used by notify") + set.IntVarP(&options.RateLimit, "rate-limit", "rl", 0, "maximum number of HTTP requests to send per second") + set.BoolVar(&options.Bulk, "bulk", false, "enable bulk processing (supported with file input") + set.IntVarP(&options.CharLimit, "char-limit", "cl", 4000, "max character limit per message") + set.StringVarP(&options.MessageFormat, "msg-format", "mf", "{{data}}", "add custom formatting to message") + set.BoolVar(&options.Silent, "silent", false, "enable silent mode") + set.BoolVarP(&options.Verbose, "verbose", "v", false, "enable verbose mode") + set.BoolVar(&options.Version, "version", false, "display version") + set.BoolVarP(&options.NoColor, "no-color", "nc", false, "disable colors in output") + set.StringVar(&options.Proxy, "proxy", "", "HTTP Proxy to use with notify") _ = set.Parse() diff --git a/go.mod b/go.mod index 1170b8b..85bf615 100644 --- a/go.mod +++ b/go.mod @@ -9,18 +9,19 @@ require ( github.com/golang/protobuf v1.4.3 // indirect github.com/google/go-cmp v0.5.4 // indirect github.com/google/uuid v1.2.0 // indirect - github.com/json-iterator/go v1.1.11 + github.com/json-iterator/go v1.1.12 github.com/klauspost/compress v1.13.1 // indirect github.com/mattn/go-isatty v0.0.13 // indirect github.com/onsi/ginkgo v1.16.4 // indirect github.com/onsi/gomega v1.10.5 // indirect + github.com/oriser/regroup v0.0.0-20210730155327-fca8d7531263 github.com/pkg/errors v0.9.1 github.com/projectdiscovery/goflags v0.0.7 github.com/projectdiscovery/gologger v1.1.4 - github.com/projectdiscovery/retryablehttp-go v1.0.1 - go.uber.org/multierr v1.1.0 + go.uber.org/multierr v1.7.0 golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect + golang.org/x/time v0.0.0-20191024005414-555d28b269f0 google.golang.org/protobuf v1.25.0 // indirect gopkg.in/yaml.v2 v2.4.0 nhooyr.io/websocket v1.8.7 // indirect diff --git a/go.sum b/go.sum index 4f61cc1..f9696c0 100644 --- a/go.sum +++ b/go.sum @@ -123,8 +123,8 @@ github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= -github.com/json-iterator/go v1.1.11/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= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -173,8 +173,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.6/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -192,6 +193,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= +github.com/oriser/regroup v0.0.0-20210730155327-fca8d7531263 h1:Qd1Ml+uEhpesT8Og0ysEhu5+DGhbhW+qxjapH8t1Kvs= +github.com/oriser/regroup v0.0.0-20210730155327-fca8d7531263/go.mod h1:odkMeLkWS8G6+WP2z3Pn2vkzhPSvBtFhAUYTKXAtZMQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -204,8 +207,6 @@ github.com/projectdiscovery/goflags v0.0.7 h1:aykmRkrOgDyRwcvGrK3qp+9aqcjGfAMs/+ github.com/projectdiscovery/goflags v0.0.7/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= github.com/projectdiscovery/gologger v1.1.4 h1:qWxGUq7ukHWT849uGPkagPKF3yBPYAsTtMKunQ8O2VI= github.com/projectdiscovery/gologger v1.1.4/go.mod h1:Bhb6Bdx2PV1nMaFLoXNBmHIU85iROS9y1tBuv7T5pMY= -github.com/projectdiscovery/retryablehttp-go v1.0.1 h1:V7wUvsZNq1Rcz7+IlcyoyQlNwshuwptuBVYWw9lx8RE= -github.com/projectdiscovery/retryablehttp-go v1.0.1/go.mod h1:SrN6iLZilNG1X4neq1D+SBxoqfAF4nyzvmevkTkWsek= github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe h1:tQTgf5XLBgZbkJDPtnV3SfdP9tzz5ZWeDBwv8WhnH9Q= github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -246,6 +247,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf 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/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -261,10 +263,12 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.coder.com/go-tools v0.0.0-20190317003359-0c6a35b74a16/go.mod h1:iKV5yK9t+J5nG9O3uF6KYdPEz3dyfMyB15MN1rbQ8Qw= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -337,6 +341,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/runner/banner.go b/internal/runner/banner.go index 4c4beb8..3408a2c 100644 --- a/internal/runner/banner.go +++ b/internal/runner/banner.go @@ -8,12 +8,12 @@ const banner = ` __ _ ___ ___ ___ / /_(_) _/_ __ / _ \/ _ \/ __/ / _/ // / -/_//_/\___/\__/_/_/ \_, / v1.0.0 +/_//_/\___/\__/_/_/ \_, / v1.0.1 /___/ ` // Version is the current version -const Version = `1.0.0` +const Version = `1.0.1` // showBanner is used to show the banner to the user func showBanner() { diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 73f6765..08d7213 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -9,12 +9,14 @@ import ( "net/url" "os" "path" + "time" "github.com/containrrr/shoutrrr" "github.com/pkg/errors" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/notify/pkg/providers" "github.com/projectdiscovery/notify/pkg/types" + "github.com/projectdiscovery/notify/pkg/utils" "gopkg.in/yaml.v2" ) @@ -59,13 +61,13 @@ func NewRunner(options *types.Options) (*Runner, error) { // Run polling and notification func (r *Runner) Run() error { - + defaultTransport := http.DefaultTransport.(*http.Transport) if r.options.Proxy != "" { proxyurl, err := url.Parse(r.options.Proxy) if err != nil || proxyurl == nil { gologger.Warning().Msgf("supplied proxy '%s' is not valid", r.options.Proxy) } else { - http.DefaultClient.Transport = &http.Transport{ + defaultTransport = &http.Transport{ Proxy: http.ProxyURL(proxyurl), ForceAttemptHTTP2: true, TLSClientConfig: &tls.Config{ @@ -75,22 +77,25 @@ func (r *Runner) Run() error { } } + if r.options.RateLimit > 0 { + http.DefaultClient.Transport = utils.NewThrottledTransport(time.Second, r.options.RateLimit, defaultTransport) + } + var inFile *os.File var err error switch { + case r.options.Data != "": + inFile, err = os.Open(r.options.Data) + if err != nil { + gologger.Fatal().Msgf("%s\n", err) + } case hasStdin(): if r.options.Bulk { gologger.Error().Msgf("bulk flag is not supported with stdin") os.Exit(1) } inFile = os.Stdin - - case r.options.Data != "": - inFile, err = os.Open(r.options.Data) - if err != nil { - gologger.Fatal().Msgf("%s\n", err) - } default: return errors.New("notify works with stdin or file using -data flag") } diff --git a/pkg/providers/discord/discord.go b/pkg/providers/discord/discord.go index 5a47b1c..880c6fd 100644 --- a/pkg/providers/discord/discord.go +++ b/pkg/providers/discord/discord.go @@ -2,13 +2,13 @@ package discord import ( "fmt" - "strings" "github.com/containrrr/shoutrrr" "github.com/pkg/errors" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/notify/pkg/utils" "go.uber.org/multierr" + "github.com/oriser/regroup" ) type Provider struct { @@ -39,20 +39,22 @@ func (p *Provider) Send(message, CliFormat string) error { for _, pr := range p.Discord { msg := utils.FormatMessage(message, utils.SelectFormat(CliFormat, pr.DiscordFormat)) - - discordTokens := strings.TrimPrefix(pr.DiscordWebHookURL, "https://discord.com/api/webhooks/") - tokens := strings.Split(discordTokens, "/") - if len(tokens) < 2 { + + discordWebhookRegex := regroup.MustCompile(`(?Phttps?):\/\/(?P(?:ptb\.|canary\.)?discord(?:app)?\.com)\/api(?:\/)?(?Pv\d{1,2})?\/webhooks\/(?P\d{17,19})\/(?P[\w\-]{68})`) + matchedGroups, err := discordWebhookRegex.Groups(pr.DiscordWebHookURL) + + if err != nil { err := fmt.Errorf("incorrect discord configuration for id: %s ", pr.ID) DiscordErr = multierr.Append(DiscordErr, err) continue } - webhookID, token := tokens[0], tokens[1] - url := fmt.Sprintf("discord://%s@%s?splitlines=no", token, webhookID) - err := shoutrrr.Send(url, msg) - if err != nil { - err = errors.Wrap(err, fmt.Sprintf("failed to send discord notification for id: %s ", pr.ID)) - DiscordErr = multierr.Append(DiscordErr, err) + + webhookID, webhookToken := matchedGroups["webhook_identifier"], matchedGroups["webhook_token"] + url := fmt.Sprintf("discord://%s@%s?splitlines=no", webhookToken, webhookID) + 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 } gologger.Verbose().Msgf("discord notification sent for id: %s", pr.ID) diff --git a/pkg/types/types.go b/pkg/types/types.go index b3606af..4f27285 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -3,14 +3,15 @@ package types import "github.com/projectdiscovery/goflags" type Options struct { - Verbose bool `yaml:"verbose,omitempty"` - NoColor bool `yaml:"no_color,omitempty"` - Silent bool `yaml:"silent,omitempty"` - Version bool `yaml:"version,omitempty"` - ProviderConfig string `yaml:"provider_config,omitempty"` + Verbose bool `yaml:"verbose,omitempty"` + NoColor bool `yaml:"no_color,omitempty"` + Silent bool `yaml:"silent,omitempty"` + Version bool `yaml:"version,omitempty"` + ProviderConfig string `yaml:"provider_config,omitempty"` Providers goflags.NormalizedStringSlice `yaml:"providers,omitempty"` IDs goflags.NormalizedStringSlice `yaml:"ids,omitempty"` - Proxy string `yaml:"proxy,omitempty"` + Proxy string `yaml:"proxy,omitempty"` + RateLimit int `yaml:"rate_limit,omitempty"` MessageFormat string `yaml:"message_format,omitempty"` diff --git a/pkg/utils/throttle.go b/pkg/utils/throttle.go new file mode 100644 index 0000000..c823895 --- /dev/null +++ b/pkg/utils/throttle.go @@ -0,0 +1,32 @@ +package utils + +import ( + "net/http" + "time" + + "golang.org/x/time/rate" +) + +// ThrottledTransport is Rate Limited HTTP Client +type ThrottledTransport struct { + roundTripperWrap http.RoundTripper + ratelimiter *rate.Limiter +} + +// RoundTrip implements the http.RoundTripper interface +func (c *ThrottledTransport) RoundTrip(r *http.Request) (*http.Response, error) { + // This is a blocking call. Honors the rate limit + err := c.ratelimiter.Wait(r.Context()) + if err != nil { + return nil, err + } + return c.roundTripperWrap.RoundTrip(r) +} + +// NewThrottledTransport wraps transport with a rate limitter +func NewThrottledTransport(limitPeriod time.Duration, requestCount int, transport http.RoundTripper) http.RoundTripper { + return &ThrottledTransport{ + roundTripperWrap: transport, + ratelimiter: rate.NewLimiter(rate.Every(limitPeriod), requestCount), + } +}