-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
allan
committed
Mar 8, 2024
1 parent
536d61d
commit e6cd907
Showing
5 changed files
with
226 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,170 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"net/textproto" | ||
|
||
"github.com/HiWay-Media/listmonk-onesignal/lib" | ||
"github.com/go-chi/chi" | ||
"github.com/knadh/listmonk/models" | ||
) | ||
|
||
type postback struct { | ||
Subject string `json:"subject"` | ||
FromEmail string `json:"from_email"` | ||
ContentType string `json:"content_type"` | ||
Body string `json:"body"` | ||
Recipients []recipient `json:"recipients"` | ||
Campaign *campaign `json:"campaign"` | ||
Attachments []attachment `json:"attachments"` | ||
} | ||
|
||
type campaign struct { | ||
FromEmail string `json:"from_email"` | ||
UUID string `json:"uuid"` | ||
Name string `json:"name"` | ||
Tags []string `json:"tags"` | ||
} | ||
|
||
type recipient struct { | ||
UUID string `json:"uuid"` | ||
Email string `json:"email"` | ||
Name string `json:"name"` | ||
Attribs models.SubscriberAttribs `json:"attribs"` | ||
Status string `json:"status"` | ||
} | ||
|
||
type attachment struct { | ||
Name string `json:"name"` | ||
Header textproto.MIMEHeader `json:"header"` | ||
Content []byte `json:"content"` | ||
} | ||
|
||
type httpResp struct { | ||
Status string `json:"status"` | ||
Message string `json:"message,omitempty"` | ||
Data interface{} `json:"data,omitempty"` | ||
} | ||
|
||
// handlePostback picks the messager based on url params and pushes message using it. | ||
func handlePostback(w http.ResponseWriter, r *http.Request) { | ||
var ( | ||
app = r.Context().Value("app").(*App) | ||
provider = chi.URLParam(r, "provider") | ||
) | ||
|
||
// Decode body | ||
body, err := ioutil.ReadAll(r.Body) | ||
if err != nil { | ||
app.logger.ErrorWith("error reading request body").Err("err", err).Write() | ||
sendErrorResponse(w, "invalid body", http.StatusBadRequest, nil) | ||
return | ||
} | ||
defer r.Body.Close() | ||
|
||
data := &postback{} | ||
if err := json.Unmarshal(body, &data); err != nil { | ||
app.logger.ErrorWith("error unmarshalling request body").Err("err", err).Write() | ||
sendErrorResponse(w, "invalid body", http.StatusBadRequest, nil) | ||
return | ||
} | ||
|
||
// Get the provider. | ||
p, ok := app.messengers[provider] | ||
if !ok { | ||
sendErrorResponse(w, "unknown provider", http.StatusBadRequest, nil) | ||
return | ||
} | ||
|
||
if len(data.Recipients) > 1 { | ||
sendErrorResponse(w, "invalid recipients", http.StatusBadRequest, nil) | ||
return | ||
} | ||
|
||
rec := data.Recipients[0] | ||
message := lib.Message{ | ||
From: data.FromEmail, | ||
Subject: data.Subject, | ||
ContentType: data.ContentType, | ||
Body: []byte(data.Body), | ||
Subscriber: models.Subscriber{ | ||
UUID: rec.UUID, | ||
Email: rec.Email, | ||
Name: rec.Name, | ||
Status: rec.Status, | ||
Attribs: rec.Attribs, | ||
}, | ||
} | ||
|
||
if data.Campaign != nil { | ||
message.Campaign = &models.Campaign{ | ||
FromEmail: data.Campaign.FromEmail, | ||
UUID: data.Campaign.UUID, | ||
Name: data.Campaign.Name, | ||
Tags: data.Campaign.Tags, | ||
} | ||
} | ||
|
||
if len(data.Attachments) > 0 { | ||
files := make([]lib.Attachment, 0, len(data.Attachments)) | ||
for _, f := range data.Attachments { | ||
a := lib.Attachment{ | ||
Name: f.Name, | ||
Header: f.Header, | ||
Content: make([]byte, len(f.Content)), | ||
} | ||
copy(a.Content, f.Content) | ||
files = append(files, a) | ||
} | ||
|
||
message.Attachments = files | ||
} | ||
|
||
app.logger.DebugWith("sending message").String("provider", provider).String("message", fmt.Sprintf("%#+v", message)).Write() | ||
|
||
// Send message. | ||
if err := p.Push(message); err != nil { | ||
app.logger.ErrorWith("error sending message").Err("err", err).Write() | ||
sendErrorResponse(w, "error sending message", http.StatusInternalServerError, nil) | ||
return | ||
} | ||
|
||
sendResponse(w, "OK") | ||
return | ||
} | ||
|
||
// wrap is a middleware that wraps HTTP handlers and injects the "app" context. | ||
func wrap(app *App, next http.HandlerFunc) http.HandlerFunc { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
ctx := context.WithValue(r.Context(), "app", app) | ||
next.ServeHTTP(w, r.WithContext(ctx)) | ||
}) | ||
} | ||
|
||
// sendResponse sends a JSON envelope to the HTTP response. | ||
func sendResponse(w http.ResponseWriter, data interface{}) { | ||
w.Header().Set("Content-Type", "application/json; charset=utf-8") | ||
out, err := json.Marshal(httpResp{Status: "success", Data: data}) | ||
if err != nil { | ||
sendErrorResponse(w, "Internal Server Error", http.StatusInternalServerError, nil) | ||
return | ||
} | ||
|
||
w.Write(out) | ||
} | ||
|
||
// sendErrorResponse sends a JSON error envelope to the HTTP response. | ||
func sendErrorResponse(w http.ResponseWriter, message string, code int, data interface{}) { | ||
w.Header().Set("Content-Type", "application/json; charset=utf-8") | ||
w.WriteHeader(code) | ||
|
||
resp := httpResp{Status: "error", | ||
Message: message, | ||
Data: data} | ||
out, _ := json.Marshal(resp) | ||
w.Write(out) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package lib | ||
|
||
import ( | ||
"net/textproto" | ||
|
||
"github.com/knadh/listmonk/models" | ||
) | ||
|
||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters