Watch
1
0
Fork
You've already forked golang-github-nicholas-fedor-shoutrrr
0
  • Go 97.9%
  • Shell 2%
  • PowerShell 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Daniel Baumann 1948361600
Releasing debian version 0.22.1+dfsg-1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2026-10-24 08:51:01 +02:00
.github Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
build Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
debian Releasing debian version 0.22.1+dfsg-1. 2026-10-24 08:51:01 +02:00
docs Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
internal Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
pkg Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
scripts Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
shoutrrr Merging upstream version 0.22.0+dfsg. 2026-10-21 13:05:05 +02:00
testing/e2e Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
.all-contributorsrc Merging upstream version 0.20.0+debian. 2026-09-13 14:43:04 +02:00
.codecov.yml Merging upstream version 0.17.0+debian. 2026-08-09 07:30:36 +02:00
.editorconfig Merging upstream version 0.15.0+debian. 2026-05-09 11:43:30 +02:00
.gitattributes Merging upstream version 0.8.17. 2025-08-10 20:39:37 +02:00
.gitignore Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
CHANGELOG.md Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
cliff.toml Merging upstream version 0.22.0+dfsg. 2026-10-21 13:05:05 +02:00
CONTRIBUTING.md Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
go.mod Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
go.sum Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00
LICENSE.md Merging upstream version 0.15.0+debian. 2026-05-09 11:43:30 +02:00
README.md Merging upstream version 0.22.0+dfsg. 2026-10-21 13:05:05 +02:00
SECURITY.md Merging upstream version 0.22.0+dfsg. 2026-10-21 13:05:05 +02:00
shoutrrr.go Merging upstream version 0.22.0+dfsg. 2026-10-21 13:05:05 +02:00
shoutrrr_test.go Merging upstream version 0.22.0+dfsg. 2026-10-21 13:05:05 +02:00
Taskfile.yml Merging upstream version 0.22.1+dfsg. 2026-10-24 08:50:42 +02:00

Shoutrrr

A notification library for gophers and their furry friends.
Heavily inspired by caronc/apprise.

OpenSSF Scorecard codecov github code size in bytes Pulls from DockerHub go.dev reference Ask DeepWiki All Contributors

license

Table of Contents

Full Documentation

Visit the project's GitHub Page for full documentation.

Installation

From Source

go install github.com/nicholas-fedor/shoutrrr/shoutrrr@latest

Binaries

Install the latest release binary to $HOME/go/bin (ensure it's in your PATH).

  • Windows (amd64):

    New-Item -ItemType Directory -Path $HOME\go\bin -Force | Out-Null; iwr (iwr https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | ConvertFrom-Json).assets.where({$_.name -like "*windows_amd64*.zip"}).browser_download_url -OutFile shoutrrr.zip; Add-Type -AssemblyName System.IO.Compression.FileSystem; ($z=[System.IO.Compression.ZipFile]::OpenRead("$PWD\shoutrrr.zip")).Entries | ? {$_.Name -eq 'shoutrrr.exe'} | % {[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$HOME\go\bin\$($_.Name)", $true)}; $z.Dispose(); rm shoutrrr.zip; if (Test-Path "$HOME\go\bin\shoutrrr.exe") { Write-Host "Successfully installed shoutrrr.exe to $HOME\go\bin" } else { Write-Host "Failed to install shoutrrr.exe" }
    
  • Linux (amd64):

     mkdir -p $HOME/go/bin && curl -L $(curl -s https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | grep -o 'https://[^"]*linux_amd64[^"]*\.tar\.gz"' | grep -o 'https://[^"]*' | head -n1) | tar -xz -C $HOME/go/bin shoutrrr
    
  • macOS (amd64):

     mkdir -p $HOME/go/bin && curl -L $(curl -s https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | grep -o 'https://[^"]*macOS_amd64[^"]*\.tar\.gz"' | grep -o 'https://[^"]*' | head -n1) | tar -xz -C $HOME/go/bin shoutrrr
    

Note

Visit the releases page for other architectures (e.g., arm, arm64, i386, riscv64).

Container Images

  • Docker Hub:

    docker pull nickfedor/shoutrrr:latest
    
  • GHCR:

    docker pull ghcr.io/nicholas-fedor/shoutrrr:latest
    

Note

Tags: latest (stable), X.Y.Z (specific version, e.g., 0.21.2), X.Y and X (latest release of a minor or major), nightly (development), platform-specific (e.g., amd64-latest, amd64-nightly).

Go Package

go get github.com/nicholas-fedor/shoutrrr@latest

Minimum Supported Version Policy

Projects importing Shoutrrr are expected to follow the latest Go minor and/or patch semantic version; ergo, Shoutrrr follows the latest minor Go version, i.e. 1.27.

GitHub Action

- name: Shoutrrr
  uses: nicholas-fedor/shoutrrr-action@v1
  with:
    url: ${{ secrets.SHOUTRRR_URL }}
    title: Deployed ${{ github.sha }}
    message: See changes at ${{ github.event.compare }}.

Usage

CLI

shoutrrr send --url "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook" --message "Hello, Slack!"

Go Package Usage

import "github.com/nicholas-fedor/shoutrrr"

err := shoutrrr.Send("slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook", "Hello, Slack!")
if err != nil {
    // Handle the error
}

Docker

docker run --rm nickfedor/shoutrrr:latest send --url "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook" --message "Hello, Slack!"

GitHub Action Usage

See installation example above.

Use as a Package

Option 1 - Using the direct send command

url := "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook"
err := shoutrrr.Send(url, "Hello world (or slack channel) !")

To pass parameters such as a title, or to cancel the send, use shoutrrr.SendContext:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

params := types.Params{}
params.SetTitle("Alert")
err := shoutrrr.SendContext(ctx, url, "Hello world (or slack channel) !", &params)

Option 2 - Using a sender

Single URL
url := "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook"
sender, err := shoutrrr.CreateSenderWithOptions(types.SenderOptions{}, url)
if err != nil {
    log.Fatal(err)
}
params := types.Params{}
for i, err := range sender.Send("Hello world (or slack channel) !", &params) { // one entry per URL, nil on success
    if err != nil {
        log.Printf("failed to send to URL %d: %v", i, err)
    }
}
Multiple URLs
urls := []string{
    "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook",
    "discord://token@channel",
}
sender, err := shoutrrr.CreateSenderWithOptions(types.SenderOptions{}, urls...)
if err != nil {
    log.Fatal(err)
}
params := types.Params{}
for i, err := range sender.Send("Hello world (or slack channel) !", &params) { // one entry per URL, nil on success
    if err != nil {
        log.Printf("failed to send to URL %d: %v", i, err)
    }
}
Custom HTTP Client and DialContext (SSRF / Egress Control)

shoutrrr.Send and shoutrrr.SendContext cannot take these options. Use NewSenderWithOptions (or CreateSenderWithOptions).

import (
    "context"
    "crypto/tls"
    "fmt"
    "log"
    "net"
    "net/http"

    "github.com/nicholas-fedor/shoutrrr"
    "github.com/nicholas-fedor/shoutrrr/pkg/types"
)

dial := func(ctx context.Context, network, addr string) (net.Conn, error) {
    host, port, err := net.SplitHostPort(addr)
    if err != nil {
        return nil, err
    }
    ips, err := net.DefaultResolver.LookupIPAddr(ctx, host)
    if err != nil {
        return nil, err
    }
    d := &net.Dialer{}
    for _, ip := range ips {
        if ip.IP.IsLoopback() || ip.IP.IsPrivate() || ip.IP.IsLinkLocalUnicast() || ip.IP.IsLinkLocalMulticast() {
            continue
        }
        conn, err := d.DialContext(ctx, network, net.JoinHostPort(ip.IP.String(), port))
        if err == nil {
            return conn, nil
        }
    }
    return nil, &net.OpError{Op: "dial", Net: network, Err: fmt.Errorf("destination blocked by egress policy")}
}

customClient := &http.Client{
    Transport: &http.Transport{
        DialContext:     dial,
        TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12},
    },
}

sender, err := shoutrrr.NewSenderWithOptions(
    nil,
    types.SenderOptions{HTTPClient: customClient, DialContext: dial},
    "discord://token@channel",
)
if err != nil {
    log.Fatal(err)
}
sender.Send("Hello with custom egress!", nil)
Message Levels

Services that support severity or priority can receive a semantic level through the level param.

Shoutrrr defines five levels: Unknown, Debug, Info, Warning, and Error. The default is Info.

params := types.Params{}
params.SetLevel(types.Warning)
params.SetTitle("Disk usage high")

errs := sender.Send("Root partition at 92%", &params)

Services that implement types.RichSender (such as Discord) receive the full []types.MessageItem slice when using SendItems, preserving level, fields, and file attachments.

Services that do not implement RichSender fall back to plain text automatically.

Per-Target Errors

Sender.Send, *ServiceRouter.SendItems, and their Context variants return one result per configured URL, including duplicate URLs, in the order the URLs were given, and the result is nil when that send succeeded. *ServiceRouter.SendAsync reports one result per configured URL in the order the sends finish. A failed send is a *types.TargetError, or wraps one, so use errors.As to get it. Its URL field holds the service ID (such as discord), never the service URL, and its Index field holds the URL's position. It supports errors.Unwrap and errors.Is. Creating a sender and locating a service return ordinary wrapped errors:

errs := sender.Send("deploy complete", nil)
for i, err := range errs {
    if err == nil {
        continue
    }
    var targetErr *types.TargetError
    if errors.As(err, &targetErr) {
        log.Printf("failed to send to URL %d (%s): %v", i, targetErr.URL, targetErr.Err)
    }
}
Context Propagation

shoutrrr.SendContext, *ServiceRouter.SendContext, and *ServiceRouter.SendItemsContext take a caller context. Services that implement types.ContextSender or types.ContextAttachmentSender receive a context derived from it with the service's send budget, so cancellation and deadlines stop their requests. For other services, the call returns when the context ends, and the service's error reports the context's error.

Format Conversion
import "github.com/nicholas-fedor/shoutrrr/pkg/format"

body, err := format.ConvertFormat("Hello **world**", "markdown", "text")

Supported conversions: text ↔ markdown ↔ html.

Use Through the CLI

shoutrrr send [FLAGS] [URL] [MESSAGE]

Use as a GitHub Action

You can also use Shoutrrr in a GitHub Actions workflow.

name: Deploy
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - [Your other workflow steps]

      - name: Shoutrrr
        uses: nicholas-fedor/shoutrrr-action@v0.0.11
        with:
          url: ${{ secrets.SHOUTRRR_URL }}
          title: Deployed ${{ github.sha }}
          message: See changes at ${{ github.event.compare }}.

Supported Services

Service Description
Bark iOS push notifications
Discord Discord webhooks
Generic Custom HTTP webhooks
Google Chat Google Chat webhooks
Gotify Gotify push notifications
Home Assistant Home Assistant REST notifications
IFTTT IFTTT webhooks
Join Join push notifications
Lark Lark (Feishu) webhooks
Logger Local logging (for testing)
Matrix Matrix rooms
Mattermost Mattermost webhooks
MQTT MQTT message broker
Notifiarr Notifiarr message forwarding
Ntfy Ntfy push notifications
Opsgenie Opsgenie alerts
PagerDuty PagerDuty incident notifications
Pushbullet Pushbullet push notifications
Pushover Pushover push notifications
Rocket.Chat Rocket.Chat webhooks
Signal Signal messages via signal-cli-rest-api
Signalgrid Signalgrid push notifications
Slack Slack webhooks or Bot API
SMTP Email notifications
Teams Microsoft Teams webhooks
Telegram Telegram bots
Twilio Twilio SMS notifications
WeCom WeCom (WeChat Work) webhook bots
XMPP XMPP chat and MUC notifications
Zulip Zulip chat

Service Discovery

Use services.SupportedSchemas() and services.SupportsSchema(schema) to enumerate or check available notification services without constructing a router:

import "github.com/nicholas-fedor/shoutrrr/pkg/services"

for _, schema := range services.SupportedSchemas() {
    fmt.Println(schema)
}

if services.SupportsSchema("discord") {
    // ...
}

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Nicholas Fedor
Nicholas Fedor

💻 📖 🚧 👀
Amir Schnell
Amir Schnell

💻
nils måsén
nils måsén

💻 📖 🚧
Luka Peschke
Luka Peschke

💻 📖
MrLuje
MrLuje

💻 📖
Simon Aronsson
Simon Aronsson

💻 📖 🚧
Arne Jørgensen
Arne Jørgensen

📖 💻
Alexei Tighineanu
Alexei Tighineanu

💻
Alexandru Bonini
Alexandru Bonini

💻
Senan Kelly
Senan Kelly

💻
JonasPf
JonasPf

💻
claycooper
claycooper

📖
Derzsi Dániel
Derzsi Dániel

💻
Joseph Kavanagh
Joseph Kavanagh

💻 🐛 ⚠️
Justin Steven
Justin Steven

🐛
Carlos Savcic
Carlos Savcic

💻 📖
PythonGermany
PythonGermany

💻
Noel O'Brien
Noel O'Brien

💻 📖
Caleb Lemoine
Caleb Lemoine

💻 📖
Eddie 'Sergeant' M
Eddie 'Sergeant' M

📖
andig
andig

🐛 💻
schnoellm
schnoellm

💻
Signalgrid
Signalgrid

💻 ⚠️

This project follows the all-contributors specification. Contributions of any kind welcome!

  • Watchtower - Automate Docker container image updates.
  • Shoutrrr GitHub Action - Notifications using Shoutrrr in GitHub Actions.
  • Bezel - A lightweight server monitoring platform that includes Docker statistics, historical data, and alert functions.
  • WatchYourLAN - Lightweight network IP scanner with web GUI.
  • DNSControl- Infrastructure as code for DNS.
  • docker-volume-backup - Backup Docker volumes locally or to any S3, WebDAV, Azure Blob Storage, Dropbox, Google Drive or SSH compatible storage.
  • BirdNET-Go - An AI solution for continuous avian monitoring and identification.