Refactor Messenger
interface.
Remove `messenger.go` and move the interface definition to `manager` and the `Message` struct to the `models` package, removing superflous and redundant message structs used in multiple places.
This commit is contained in:
parent
9ffc912a2c
commit
917696a543
11 changed files with 84 additions and 108 deletions
|
@ -31,7 +31,6 @@ import (
|
|||
"github.com/knadh/listmonk/internal/media"
|
||||
"github.com/knadh/listmonk/internal/media/providers/filesystem"
|
||||
"github.com/knadh/listmonk/internal/media/providers/s3"
|
||||
"github.com/knadh/listmonk/internal/messenger"
|
||||
"github.com/knadh/listmonk/internal/messenger/email"
|
||||
"github.com/knadh/listmonk/internal/messenger/postback"
|
||||
"github.com/knadh/listmonk/internal/subimporter"
|
||||
|
@ -484,7 +483,7 @@ func initImporter(q *models.Queries, db *sqlx.DB, app *App) *subimporter.Importe
|
|||
}
|
||||
|
||||
// initSMTPMessenger initializes the SMTP messenger.
|
||||
func initSMTPMessenger(m *manager.Manager) messenger.Messenger {
|
||||
func initSMTPMessenger(m *manager.Manager) manager.Messenger {
|
||||
var (
|
||||
mapKeys = ko.MapKeys("smtp")
|
||||
servers = make([]email.Server, 0, len(mapKeys))
|
||||
|
@ -526,13 +525,13 @@ func initSMTPMessenger(m *manager.Manager) messenger.Messenger {
|
|||
|
||||
// initPostbackMessengers initializes and returns all the enabled
|
||||
// HTTP postback messenger backends.
|
||||
func initPostbackMessengers(m *manager.Manager) []messenger.Messenger {
|
||||
func initPostbackMessengers(m *manager.Manager) []manager.Messenger {
|
||||
items := ko.Slices("messengers")
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var out []messenger.Messenger
|
||||
var out []manager.Messenger
|
||||
for _, item := range items {
|
||||
if !item.Bool("enabled") {
|
||||
continue
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/knadh/listmonk/internal/i18n"
|
||||
"github.com/knadh/listmonk/internal/manager"
|
||||
"github.com/knadh/listmonk/internal/media"
|
||||
"github.com/knadh/listmonk/internal/messenger"
|
||||
"github.com/knadh/listmonk/internal/subimporter"
|
||||
"github.com/knadh/listmonk/models"
|
||||
"github.com/knadh/paginator"
|
||||
|
@ -43,7 +42,7 @@ type App struct {
|
|||
constants *constants
|
||||
manager *manager.Manager
|
||||
importer *subimporter.Importer
|
||||
messengers map[string]messenger.Messenger
|
||||
messengers map[string]manager.Messenger
|
||||
media media.Store
|
||||
i18n *i18n.I18n
|
||||
bounce *bounce.Manager
|
||||
|
@ -167,7 +166,7 @@ func main() {
|
|||
db: db,
|
||||
constants: initConstants(),
|
||||
media: initMediaStore(),
|
||||
messengers: make(map[string]messenger.Messenger),
|
||||
messengers: make(map[string]manager.Messenger),
|
||||
log: lo,
|
||||
bufLog: bufLog,
|
||||
captcha: initCaptcha(),
|
||||
|
|
|
@ -3,7 +3,7 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/knadh/listmonk/internal/manager"
|
||||
"github.com/knadh/listmonk/models"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -32,7 +32,7 @@ func (app *App) sendNotification(toEmails []string, subject, tplName string, dat
|
|||
return err
|
||||
}
|
||||
|
||||
m := manager.Message{}
|
||||
m := models.Message{}
|
||||
m.ContentType = app.notifTpls.contentType
|
||||
m.From = app.constants.FromEmail
|
||||
m.To = toEmails
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/knadh/listmonk/internal/i18n"
|
||||
"github.com/knadh/listmonk/internal/messenger"
|
||||
"github.com/knadh/listmonk/internal/manager"
|
||||
"github.com/knadh/listmonk/models"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/lib/pq"
|
||||
|
@ -566,17 +566,17 @@ func handleSelfExportSubscriberData(c echo.Context) error {
|
|||
|
||||
// Send the data as a JSON attachment to the subscriber.
|
||||
const fname = "data.json"
|
||||
if err := app.messengers[emailMsgr].Push(messenger.Message{
|
||||
if err := app.messengers[emailMsgr].Push(models.Message{
|
||||
ContentType: app.notifTpls.contentType,
|
||||
From: app.constants.FromEmail,
|
||||
To: []string{data.Email},
|
||||
Subject: app.i18n.Ts("email.data.title"),
|
||||
Body: msg.Bytes(),
|
||||
Attachments: []messenger.Attachment{
|
||||
Attachments: []models.Attachment{
|
||||
{
|
||||
Name: fname,
|
||||
Content: b,
|
||||
Header: messenger.MakeAttachmentHeader(fname, "base64"),
|
||||
Header: manager.MakeAttachmentHeader(fname, "base64"),
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"github.com/knadh/koanf/parsers/json"
|
||||
"github.com/knadh/koanf/providers/rawbytes"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/knadh/listmonk/internal/messenger"
|
||||
"github.com/knadh/listmonk/internal/messenger/email"
|
||||
"github.com/knadh/listmonk/models"
|
||||
"github.com/labstack/echo/v4"
|
||||
|
@ -250,7 +249,7 @@ func handleTestSMTPSettings(c echo.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
m := messenger.Message{}
|
||||
m := models.Message{}
|
||||
m.ContentType = app.notifTpls.contentType
|
||||
m.From = app.constants.FromEmail
|
||||
m.To = []string{to}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/knadh/listmonk/internal/manager"
|
||||
"github.com/knadh/listmonk/internal/messenger"
|
||||
"github.com/knadh/listmonk/models"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
@ -56,9 +55,9 @@ func handleSendTxMessage(c echo.Context) error {
|
|||
app.i18n.Ts("globals.messages.invalidFields", "name", fmt.Sprintf("file: %s", err.Error())))
|
||||
}
|
||||
|
||||
m.Attachments = append(m.Attachments, models.TxAttachment{
|
||||
m.Attachments = append(m.Attachments, models.Attachment{
|
||||
Name: f.Filename,
|
||||
Header: messenger.MakeAttachmentHeader(f.Filename, "base64"),
|
||||
Header: manager.MakeAttachmentHeader(f.Filename, "base64"),
|
||||
Content: b,
|
||||
})
|
||||
}
|
||||
|
@ -121,7 +120,7 @@ func handleSendTxMessage(c echo.Context) error {
|
|||
}
|
||||
|
||||
// Prepare the final message.
|
||||
msg := manager.Message{}
|
||||
msg := models.Message{}
|
||||
msg.Subscriber = sub
|
||||
msg.To = []string{sub.Email}
|
||||
msg.From = m.FromEmail
|
||||
|
@ -130,7 +129,7 @@ func handleSendTxMessage(c echo.Context) error {
|
|||
msg.Messenger = m.Messenger
|
||||
msg.Body = m.Body
|
||||
for _, a := range m.Attachments {
|
||||
msg.Attachments = append(msg.Attachments, messenger.Attachment{
|
||||
msg.Attachments = append(msg.Attachments, models.Attachment{
|
||||
Name: a.Name,
|
||||
Header: a.Header,
|
||||
Content: a.Content,
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/knadh/listmonk/internal/i18n"
|
||||
"github.com/knadh/listmonk/internal/messenger"
|
||||
"github.com/knadh/listmonk/models"
|
||||
"github.com/paulbellamy/ratecounter"
|
||||
)
|
||||
|
@ -40,6 +39,15 @@ type Store interface {
|
|||
DeleteSubscriber(id int64) error
|
||||
}
|
||||
|
||||
// Messenger is an interface for a generic messaging backend,
|
||||
// for instance, e-mail, SMS etc.
|
||||
type Messenger interface {
|
||||
Name() string
|
||||
Push(models.Message) error
|
||||
Flush() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
// CampStats contains campaign stats like per minute send rate.
|
||||
type CampStats struct {
|
||||
SendRate int
|
||||
|
@ -51,7 +59,7 @@ type Manager struct {
|
|||
cfg Config
|
||||
store Store
|
||||
i18n *i18n.I18n
|
||||
messengers map[string]messenger.Messenger
|
||||
messengers map[string]Messenger
|
||||
notifCB models.AdminNotifCallback
|
||||
logger *log.Logger
|
||||
|
||||
|
@ -73,7 +81,7 @@ type Manager struct {
|
|||
campMsgQueue chan CampaignMessage
|
||||
campMsgErrorQueue chan msgError
|
||||
campMsgErrorCounts map[int]int
|
||||
msgQueue chan Message
|
||||
msgQueue chan models.Message
|
||||
|
||||
// Sliding window keeps track of the total number of messages sent in a period
|
||||
// and on reaching the specified limit, waits until the window is over before
|
||||
|
@ -98,14 +106,6 @@ type CampaignMessage struct {
|
|||
unsubURL string
|
||||
}
|
||||
|
||||
// Message represents a generic message to be pushed to a messenger.
|
||||
type Message struct {
|
||||
messenger.Message
|
||||
|
||||
// Messenger is the messenger backend to use: email|postback.
|
||||
Messenger string
|
||||
}
|
||||
|
||||
// Config has parameters for configuring the manager.
|
||||
type Config struct {
|
||||
// Number of subscribers to pull from the DB in a single iteration.
|
||||
|
@ -163,14 +163,14 @@ func New(cfg Config, store Store, notifCB models.AdminNotifCallback, i *i18n.I18
|
|||
i18n: i,
|
||||
notifCB: notifCB,
|
||||
logger: l,
|
||||
messengers: make(map[string]messenger.Messenger),
|
||||
messengers: make(map[string]Messenger),
|
||||
camps: make(map[int]*models.Campaign),
|
||||
campRates: make(map[int]*ratecounter.RateCounter),
|
||||
tpls: make(map[int]*models.Template),
|
||||
links: make(map[string]string),
|
||||
subFetchQueue: make(chan *models.Campaign, cfg.Concurrency),
|
||||
campMsgQueue: make(chan CampaignMessage, cfg.Concurrency*2),
|
||||
msgQueue: make(chan Message, cfg.Concurrency),
|
||||
msgQueue: make(chan models.Message, cfg.Concurrency),
|
||||
campMsgErrorQueue: make(chan msgError, cfg.MaxSendErrors),
|
||||
campMsgErrorCounts: make(map[int]int),
|
||||
slidingWindowStart: time.Now(),
|
||||
|
@ -202,7 +202,7 @@ func (m *Manager) NewCampaignMessage(c *models.Campaign, s models.Subscriber) (C
|
|||
}
|
||||
|
||||
// AddMessenger adds a Messenger messaging backend to the manager.
|
||||
func (m *Manager) AddMessenger(msg messenger.Messenger) error {
|
||||
func (m *Manager) AddMessenger(msg Messenger) error {
|
||||
id := msg.Name()
|
||||
if _, ok := m.messengers[id]; ok {
|
||||
return fmt.Errorf("messenger '%s' is already loaded", id)
|
||||
|
@ -213,7 +213,7 @@ func (m *Manager) AddMessenger(msg messenger.Messenger) error {
|
|||
|
||||
// PushMessage pushes an arbitrary non-campaign Message to be sent out by the workers.
|
||||
// It times out if the queue is busy.
|
||||
func (m *Manager) PushMessage(msg Message) error {
|
||||
func (m *Manager) PushMessage(msg models.Message) error {
|
||||
t := time.NewTicker(pushTimeout)
|
||||
defer t.Stop()
|
||||
|
||||
|
@ -355,7 +355,7 @@ func (m *Manager) worker() {
|
|||
numMsg++
|
||||
|
||||
// Outgoing message.
|
||||
out := messenger.Message{
|
||||
out := models.Message{
|
||||
From: msg.from,
|
||||
To: []string{msg.to},
|
||||
Subject: msg.subject,
|
||||
|
@ -410,7 +410,7 @@ func (m *Manager) worker() {
|
|||
return
|
||||
}
|
||||
|
||||
err := m.messengers[msg.Messenger].Push(msg.Message)
|
||||
err := m.messengers[msg.Messenger].Push(msg)
|
||||
if err != nil {
|
||||
m.logger.Printf("error sending message '%s': %v", msg.Subject, err)
|
||||
}
|
||||
|
@ -801,3 +801,17 @@ func (m *Manager) makeGnericFuncMap() template.FuncMap {
|
|||
|
||||
return f
|
||||
}
|
||||
|
||||
// MakeAttachmentHeader is a helper function that returns a
|
||||
// textproto.MIMEHeader tailored for attachments, primarily
|
||||
// email. If no encoding is given, base64 is assumed.
|
||||
func MakeAttachmentHeader(filename, encoding string) textproto.MIMEHeader {
|
||||
if encoding == "" {
|
||||
encoding = "base64"
|
||||
}
|
||||
h := textproto.MIMEHeader{}
|
||||
h.Set("Content-Disposition", "attachment; filename="+filename)
|
||||
h.Set("Content-Type", "application/json; name=\""+filename+"\"")
|
||||
h.Set("Content-Transfer-Encoding", encoding)
|
||||
return h
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"net/smtp"
|
||||
"net/textproto"
|
||||
|
||||
"github.com/knadh/listmonk/internal/messenger"
|
||||
"github.com/knadh/listmonk/models"
|
||||
"github.com/knadh/smtppool"
|
||||
)
|
||||
|
||||
|
@ -92,7 +92,7 @@ func (e *Emailer) Name() string {
|
|||
}
|
||||
|
||||
// Push pushes a message to the server.
|
||||
func (e *Emailer) Push(m messenger.Message) error {
|
||||
func (e *Emailer) Push(m models.Message) error {
|
||||
// If there are more than one SMTP servers, send to a random
|
||||
// one from the list.
|
||||
var (
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
package messenger
|
||||
|
||||
import (
|
||||
"net/textproto"
|
||||
|
||||
"github.com/knadh/listmonk/models"
|
||||
)
|
||||
|
||||
// Messenger is an interface for a generic messaging backend,
|
||||
// for instance, e-mail, SMS etc.
|
||||
type Messenger interface {
|
||||
Name() string
|
||||
Push(Message) error
|
||||
Flush() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Message is the message pushed to a Messenger.
|
||||
type Message struct {
|
||||
From string
|
||||
To []string
|
||||
Subject string
|
||||
ContentType string
|
||||
Body []byte
|
||||
AltBody []byte
|
||||
Headers textproto.MIMEHeader
|
||||
Attachments []Attachment
|
||||
|
||||
Subscriber models.Subscriber
|
||||
|
||||
// Campaign is generally the same instance for a large number of subscribers.
|
||||
Campaign *models.Campaign
|
||||
}
|
||||
|
||||
// Attachment represents a file or blob attachment that can be
|
||||
// sent along with a message by a Messenger.
|
||||
type Attachment struct {
|
||||
Name string
|
||||
Header textproto.MIMEHeader
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// MakeAttachmentHeader is a helper function that returns a
|
||||
// textproto.MIMEHeader tailored for attachments, primarily
|
||||
// email. If no encoding is given, base64 is assumed.
|
||||
func MakeAttachmentHeader(filename, encoding string) textproto.MIMEHeader {
|
||||
if encoding == "" {
|
||||
encoding = "base64"
|
||||
}
|
||||
h := textproto.MIMEHeader{}
|
||||
h.Set("Content-Disposition", "attachment; filename="+filename)
|
||||
h.Set("Content-Type", "application/json; name=\""+filename+"\"")
|
||||
h.Set("Content-Transfer-Encoding", encoding)
|
||||
return h
|
||||
}
|
|
@ -10,11 +10,11 @@ import (
|
|||
"net/textproto"
|
||||
"time"
|
||||
|
||||
"github.com/knadh/listmonk/internal/messenger"
|
||||
"github.com/knadh/listmonk/models"
|
||||
)
|
||||
|
||||
// postback is the payload that's posted as JSON to the HTTP Postback server.
|
||||
//
|
||||
//easyjson:json
|
||||
type postback struct {
|
||||
Subject string `json:"subject"`
|
||||
|
@ -94,7 +94,7 @@ func (p *Postback) Name() string {
|
|||
}
|
||||
|
||||
// Push pushes a message to the server.
|
||||
func (p *Postback) Push(m messenger.Message) error {
|
||||
func (p *Postback) Push(m models.Message) error {
|
||||
pb := postback{
|
||||
Subject: m.Subject,
|
||||
ContentType: m.ContentType,
|
||||
|
|
|
@ -347,6 +347,34 @@ type Bounce struct {
|
|||
Total int `db:"total" json:"-"`
|
||||
}
|
||||
|
||||
// Message is the message pushed to a Messenger.
|
||||
type Message struct {
|
||||
From string
|
||||
To []string
|
||||
Subject string
|
||||
ContentType string
|
||||
Body []byte
|
||||
AltBody []byte
|
||||
Headers textproto.MIMEHeader
|
||||
Attachments []Attachment
|
||||
|
||||
Subscriber Subscriber
|
||||
|
||||
// Campaign is generally the same instance for a large number of subscribers.
|
||||
Campaign *Campaign
|
||||
|
||||
// Messenger is the messenger backend to use: email|postback.
|
||||
Messenger string
|
||||
}
|
||||
|
||||
// Attachment represents a file or blob attachment that can be
|
||||
// sent along with a message by a Messenger.
|
||||
type Attachment struct {
|
||||
Name string
|
||||
Header textproto.MIMEHeader
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// TxMessage represents an e-mail campaign.
|
||||
type TxMessage struct {
|
||||
SubscriberEmails []string `json:"subscriber_emails"`
|
||||
|
@ -364,7 +392,7 @@ type TxMessage struct {
|
|||
Messenger string `json:"messenger"`
|
||||
|
||||
// File attachments added from multi-part form data.
|
||||
Attachments []TxAttachment `json:"-"`
|
||||
Attachments []Attachment `json:"-"`
|
||||
|
||||
Subject string `json:"-"`
|
||||
Body []byte `json:"-"`
|
||||
|
@ -372,13 +400,6 @@ type TxMessage struct {
|
|||
SubjectTpl *txttpl.Template `json:"-"`
|
||||
}
|
||||
|
||||
// TxAttachment is used by TxMessage, consists of FileName and file Content in bytes
|
||||
type TxAttachment struct {
|
||||
Name string
|
||||
Header textproto.MIMEHeader
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// markdown is a global instance of Markdown parser and renderer.
|
||||
var markdown = goldmark.New(
|
||||
goldmark.WithParserOptions(
|
||||
|
|
Loading…
Reference in a new issue