notify

package module
v2.0.0-...-4a423d9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2023 License: MIT Imports: 6 Imported by: 0

README

notify logo

codecov Go Report Card Codacy Badge Maintainability go.dev reference

A dead simple Go library for sending notifications to various messaging services.

Notify v2

This is the home branch of Notify v2. It is currently in active development and not yet ready for production use. The main branch will stay the default branch until v2 is ready for production use. At this point, the main branch will be renamed to v1 and the v2 branch will be merged into the main branch.

Notify v2 lets you enjoy the simplicity of Notify v1 with more power and flexibility at your hands. Providing a simple interface that lets you send attachments, define custom message renderers and dynamic message enrichment.

About

Notify was born out of my own need to have my API servers running in production be able to notify me when critical errors occur. Of course, Notify can be used for any other purpose as well. The library is kept as simple as possible for quick integration and ease of use.

Disclaimer

Usage of this library should comply with the stated rules and the terms present in the license and code of conduct. Failure to comply, including but not limited to misuse or spamming, can result in permanent banning from all supported platforms as governed by the respective platform's rules and regulations.

Notify's functionality is determined by the consistency of the supported external services and their corresponding latest client libraries; it may change without notice. This fact, coupled with the inevitable inconsistencies that can arise, dictates that Notify should not be used in situations where its failure or inconsistency could result in significant damage, loss, or disruption. Always have a backup plan and only use Notify after understanding and accepting these conditions.

Please read the license for a complete understanding of the permissions, conditions, and limitations of use.

Install

go get -u github.com/nikoksr/notify/v2

Example usage

You can use Notify just like you're used to from v1. A simple example in which we send a notification to a Telegram channel could look like this:


func main() {
    // Create a new telegram service. We're using the new constructor option WithRecipients() to specify the recipients. We can,
    // however, also rely on the old way of doing things and add the recipients to the service later on using the AddRecipients()
    // method.
    svc, _ := telegram.New(token,
        telegram.WithRecipients(recipient),
    )

	// Create the actual notify instance and pass the telegram service to it. Again, we're making use of the new constructor
	// option WithServices() to specify the services. UseServices() is still available and can be used to add services later
	// on.
    dispatcher := notify.New(
        notify.WithServices(svc),
    )

	// Send a notification
    _ = dispatcher.Send(context.Background(),
        "Subject/Title",
        "The actual message - Hello, you awesome gophers! :)",
    )
}

We touched a little bit on what's new in v2 in the example above. Let's take a deeper dive into the new, more advanced features.

In this example, we're going to send a notification to a Discord channel. We're going to make use of the new discord.Webhook service, which allows us to send notifications to Discord webhooks. We're also going to define a custom message renderer, which allows us to define how the message should look like. Lastly, we're going to send a couple of attachments and metadata along with the notification.

func main() {
    // Create a new discord webhook service.
    svc, _ := discord.NewWebhook(
        discord.WithRecipients(webhookURL),
        discord.WithMessageRenderer(customRenderer),
    )

	// Open a couple of files to send as attachments.
    img, _ := os.Open("/path/to/image.png")
	defer img.Close()

    txt, _ := os.Open("/path/to/text.txt")
	defer txt.Close()

	// Create some example metadata that we make use of in our custom renderer.
    exampleMetadata := map[string]interface{}{
        "foo":  "bar",
    }

	// Send a notification with the attachments and metadata. In this case, we're using the service directly.
    _ = svc.Send(ctx,
        "[Test] Notify v2",
        "Hello, you awesome gophers! :)",
        notify.SendWithAttachments(img, txt),
        notify.SendWithMetadata(exampleMetadata),
    )
}


// The custom renderer allows us to define how the message should look like. The respective SendConfig is passed to the
// renderer, which contains all the information we need to render the message.
func customRenderer(conf discord.SendConfig) string {
    var builder strings.Builder

	// For demo purposes, we're just going to marshal the metadata to human-readable JSON and add it to the message.
    metadata, _ := json.MarshalIndent(conf.Metadata(), "", "  ")

	// Put together the message.
    builder.WriteString(conf.Subject())
    builder.WriteString("\n\n")
    builder.WriteString(conf.Message())
    builder.WriteString("\n\n")
	builder.WriteString("Metadata:\n")
	builder.WriteString(string(metadata))
	builder.WriteString("\n\n")
    builder.WriteString("<-- A super necessary footer -->\n")

    return builder.String()
}

Also note that we're using a Dispatcher in the first example, which allows us to send notifications to multiple services at once. In the second example, we're using a Service directly, which only allows us to send notifications to a single service.

Contributing

Yes, please! Contributions of all kinds are very welcome! Feel free to check our open issues. Please also take a look at the contribution guidelines.

Psst, don't forget to check the list of missing services waiting to be added by you or create a new issue if you want a new service to be added.

Supported services

Click here to request a missing service.

Service Path Credits Tested
Discord service/discord bwmarrin/discordgo
Mail service/mail xhit/go-simple-mail/v2
Ntfy service/ntfy -
Slack service/slack slack-go/slack
Telegram service/telegram go-telegram-bot-api/telegram-bot-api
Twilio service/twilio twilio/twilio-go

Special Thanks

Maintainers

The logo was made by the amazing MariaLetta.

Similar projects

Just to clarify, Notify was not inspired by any other project. I created it as a tiny subpackage of a larger project and only later decided to make it a standalone project. In this section I just want to mention other great projects.

Show your support

Please give a ⭐️ if you like the project! It draws more attention to the project, which helps us improve it even faster.

Documentation

Overview

Package notify provides an abstraction for sending notifications through multiple services. It allows easy addition of new services and uniform handling of notification sending.

Index

Constants

This section is empty.

Variables

View Source
var DefaultDispatcher = New()

DefaultDispatcher is the default Dispatcher instance.

View Source
var ErrNoRecipients = errors.New("no recipients specified")

ErrNoRecipients indicates that there are no recipients specified for a service.

Functions

func Send

func Send(ctx context.Context, subject, message string, opts ...SendOption) error

Send sends a notification with the given subject and message through all the services of the defaultNotify instance. It performs these operations concurrently and returns the first encountered error, if any.

func UseServices

func UseServices(services ...Service)

UseServices appends the given service(s) to the defaultNotify instance's services list. Nil services are ignored.

Types

type Attachment

type Attachment interface {
	// Reader is used to read the contents of the attachment.
	io.Reader

	// Name is used as the filename when sending the attachment.
	Name() string
}

Attachment represents a file that can be attached to a notification message.

type BadRequestError

type BadRequestError struct {
	// Cause is the underlying error that caused the bad request error.
	Cause error
}

BadRequestError indicates that the request to the remote service was incorrect.

func (*BadRequestError) Error

func (e *BadRequestError) Error() string

Error provides the string representation of the BadRequestError error.

func (*BadRequestError) Unwrap

func (e *BadRequestError) Unwrap() error

Unwrap retrieves the underlying error for the BadRequestError error.

type Dispatcher

type Dispatcher struct {
	// contains filtered or unexported fields
}

Dispatcher handles notifications sending through multiple services.

func New

func New(opts ...Option) *Dispatcher

New creates a Dispatcher instance with the specified options. If no options are provided, a Dispatcher instance is created with no services and default options.

func (*Dispatcher) Send

func (d *Dispatcher) Send(ctx context.Context, subject, message string, opts ...SendOption) error

Send sends a notification with the given subject and message through all the services of n. It performs these operations concurrently and returns the first encountered error, if any.

func (*Dispatcher) UseServices

func (d *Dispatcher) UseServices(services ...Service)

UseServices appends the given service(s) to the Dispatcher instance's services list. Nil services are ignored.

type Option

type Option = func(*Dispatcher)

Option is a function that configures a Dispatcher instance.

func WithServices

func WithServices(services ...Service) Option

WithServices adds the given services to a Dispatcher instance's services list. The services will be used in the order they are provided. Nil services are ignored.

type RateLimitError

type RateLimitError struct {
	// Cause is the underlying error that caused the rate limit exceeded error.
	Cause error
}

RateLimitError indicates that the rate limit for the service has been exceeded.

func (*RateLimitError) Error

func (e *RateLimitError) Error() string

Error provides the string representation of the RateLimitError error.

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

Unwrap retrieves the underlying error for the RateLimitError error.

type SendConfig

type SendConfig interface {
	// SetAttachments sets attachments that can be sent alongside the message.
	SetAttachments(attachments ...Attachment)

	// SetMetadata sets additional metadata that can be sent with the message.
	SetMetadata(metadata map[string]any)

	// SetDryRun sets the dry run flag.
	SetDryRun(dryRun bool)

	// SetContinueOnErr sets the continue on error flag.
	SetContinueOnErr(continueOnError bool)
}

SendConfig is used to configure the Send call.

type SendError

type SendError struct {
	FailedRecipients []string
	Errors           []error
}

SendError encapsulates any errors that occur when sending a notification.

func (*SendError) Error

func (e *SendError) Error() string

Error provides the string representation of the SendError error.

type SendOption

type SendOption = func(SendConfig)

SendOption is a function that modifies the configuration of a Send call.

func SendWithAttachments

func SendWithAttachments(attachments ...Attachment) SendOption

SendWithAttachments attaches the provided files to the message being sent.

func SendWithContinueOnErr

func SendWithContinueOnErr(continueOnErr bool) SendOption

SendWithContinueOnErr sets the continue on error flag. If set to true, the service will continue sending the message to the next recipient even if an error occurred.

func SendWithDryRun

func SendWithDryRun(dryRun bool) SendOption

SendWithDryRun sets the dry run flag. If set to true, the service will not try to authenticate or send the message.

func SendWithMetadata

func SendWithMetadata(metadata map[string]any) SendOption

SendWithMetadata attaches the provided metadata to the message being sent.

type Service

type Service interface {
	// Name should return a unique identifier for the service.
	Name() string

	// Send sends a message with a subject through this service.
	// Additional options can be provided to customize the sending process.
	// Returns an error if the sending process failed.
	Send(ctx context.Context, subject string, message string, opts ...SendOption) error
}

Service describes a notification service that can send messages. Each service implementation should provide its own way of sending messages.

type ServiceFailureError

type ServiceFailureError struct {
	// Service is the name of the service that failed.
	Service string
	// Cause is the underlying error that caused the service to fail.
	Cause error
}

ServiceFailureError represents an error that occurs when a service fails.

func (*ServiceFailureError) Error

func (e *ServiceFailureError) Error() string

Error provides the string representation of the ServiceFailureError error.

func (*ServiceFailureError) Unwrap

func (e *ServiceFailureError) Unwrap() error

Unwrap retrieves the underlying error for the ServiceFailureError error.

type UnauthorizedError

type UnauthorizedError struct {
	// Cause is the underlying error that caused the unauthorized error.
	Cause error
}

UnauthorizedError indicates that the user is not authorized to perform the requested action.

func (*UnauthorizedError) Error

func (e *UnauthorizedError) Error() string

Error provides the string representation of the UnauthorizedError error.

func (*UnauthorizedError) Unwrap

func (e *UnauthorizedError) Unwrap() error

Unwrap retrieves the underlying error for the UnauthorizedError error.

Directories

Path Synopsis
service

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL