Extend 'order_placed' endpoint to handle different item categories
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
5cdddc21ed
commit
51211e641b
|
@ -0,0 +1,24 @@
|
||||||
|
package pretix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Server) buildURL(endpoint string, args ...any) string {
|
||||||
|
baseURL := strings.TrimSuffix(s.config.BaseURL, "/")
|
||||||
|
return fmt.Sprintf(endpoint, append([]any{baseURL}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) doGetRequest(url string) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
req.Header.Set("Authorization", fmt.Sprint("Token ", s.config.APIToken))
|
||||||
|
|
||||||
|
return s.client.Do(req)
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package pretix
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -15,8 +16,17 @@ const (
|
||||||
eventOrderPlaced = "pretix.event.order.placed"
|
eventOrderPlaced = "pretix.event.order.placed"
|
||||||
|
|
||||||
urlIndividualOrder = "%s/api/v1/organizers/%s/events/%s/orders/%s/"
|
urlIndividualOrder = "%s/api/v1/organizers/%s/events/%s/orders/%s/"
|
||||||
|
urlItem = "%s/api/v1/organizers/%s/events/%s/items/%d/"
|
||||||
|
urlItemCategory = "%s/api/v1/organizers/%s/events/%s/categories/%d/"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var adverbs = []string{" Außerdem ", " Zusätzlich ", " Weiterhin ", " Des Weiteren "}
|
||||||
|
|
||||||
|
type categoryBin struct {
|
||||||
|
NumFree uint
|
||||||
|
NumPaid uint
|
||||||
|
}
|
||||||
|
|
||||||
func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Header.Get("Content-Type") != "application/json" {
|
if r.Header.Get("Content-Type") != "application/json" {
|
||||||
writeStatus(w, http.StatusBadRequest)
|
writeStatus(w, http.StatusBadRequest)
|
||||||
|
@ -42,16 +52,7 @@ func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, s.buildURL(urlIndividualOrder, data.Organizer, data.Event, data.Code), nil)
|
resp, err := s.doGetRequest(s.buildURL(urlIndividualOrder, data.Organizer, data.Event, data.Code))
|
||||||
if err != nil {
|
|
||||||
writeStatus(w, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Accept", "application/json")
|
|
||||||
req.Header.Set("Authorization", fmt.Sprint("Token ", s.config.APIToken))
|
|
||||||
|
|
||||||
resp, err := s.client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeStatus(w, http.StatusInternalServerError)
|
writeStatus(w, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -73,6 +74,7 @@ func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
||||||
Code string
|
Code string
|
||||||
Datetime time.Time
|
Datetime time.Time
|
||||||
Positions []struct{
|
Positions []struct{
|
||||||
|
Item uint
|
||||||
Price string
|
Price string
|
||||||
}
|
}
|
||||||
Total string
|
Total string
|
||||||
|
@ -97,59 +99,80 @@ func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if rowsAffected > 0 && order.Datetime.Add(72 * time.Hour).After(time.Now()) { // pretix retries failed webhooks for up to three days
|
if rowsAffected > 0 && order.Datetime.Add(72 * time.Hour).After(time.Now()) { // pretix retries failed webhooks for up to three days
|
||||||
numFree := 0
|
categories := map[string]categoryBin{}
|
||||||
numPaid := 0
|
|
||||||
for _, position := range order.Positions {
|
for _, position := range order.Positions {
|
||||||
euros, cents, err := parsePrice(position.Price)
|
euros, cents, err := parsePrice(position.Price)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name := s.fetchCategory(data.Organizer, data.Event, position.Item)
|
||||||
|
|
||||||
|
category := categories[name]
|
||||||
if euros > 0 || cents > 0 {
|
if euros > 0 || cents > 0 {
|
||||||
numPaid++
|
category.NumPaid++
|
||||||
} else {
|
} else {
|
||||||
numFree++
|
category.NumFree++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
categories[name] = category
|
||||||
}
|
}
|
||||||
|
|
||||||
var verb, free, conjunction, paid, noun, total string
|
var body strings.Builder
|
||||||
if numFree+numPaid == 1 {
|
body.WriteString("\U0001f389")
|
||||||
verb = "wurde "
|
|
||||||
} else {
|
rng := rand.New(rand.NewSource(time.Now().Unix()))
|
||||||
verb = "wurden "
|
|
||||||
}
|
adverb := " Es "
|
||||||
if numFree == 1 {
|
for category, bin := range categories {
|
||||||
free = "ein kostenloses "
|
var verb, free, conjunction, paid, noun, preposition, quote, total string
|
||||||
} else if numFree > 1 {
|
if bin.NumFree+bin.NumPaid == 1 {
|
||||||
free = fmt.Sprint(numFree, " kostenlose ")
|
verb = "wurde "
|
||||||
}
|
} else {
|
||||||
if numPaid == 1 {
|
verb = "wurden "
|
||||||
paid = "ein bezahltes "
|
|
||||||
} else if numPaid > 1 {
|
|
||||||
paid = fmt.Sprint(numPaid, " bezahlte ")
|
|
||||||
}
|
|
||||||
if numFree > 0 && numPaid > 0 {
|
|
||||||
conjunction = "und "
|
|
||||||
}
|
|
||||||
if numPaid == 1 || numPaid == 0 && numFree == 1 { // match number of adjective immediately before noun
|
|
||||||
noun = "Ticket "
|
|
||||||
} else {
|
|
||||||
noun = "Tickets "
|
|
||||||
}
|
|
||||||
if totalEuros, totalCents, err := parsePrice(order.Total); err == nil && (totalEuros > 0 || totalCents > 0) {
|
|
||||||
var adverb, fraction string
|
|
||||||
if numPaid > 1 {
|
|
||||||
adverb = "insgesamt "
|
|
||||||
}
|
}
|
||||||
if totalCents > 0 {
|
if bin.NumFree == 1 {
|
||||||
fraction = fmt.Sprintf(",%02d", totalCents)
|
free = "ein kostenloses "
|
||||||
|
} else if bin.NumFree > 1 {
|
||||||
|
free = fmt.Sprint(bin.NumFree, " kostenlose ")
|
||||||
}
|
}
|
||||||
total = fmt.Sprint("für ", adverb, totalEuros, fraction, " Euro ")
|
if bin.NumPaid == 1 {
|
||||||
|
paid = "ein bezahltes "
|
||||||
|
} else if bin.NumPaid > 1 {
|
||||||
|
paid = fmt.Sprint(bin.NumPaid, " bezahlte ")
|
||||||
|
}
|
||||||
|
if bin.NumFree > 0 && bin.NumPaid > 0 {
|
||||||
|
conjunction = "und "
|
||||||
|
}
|
||||||
|
if bin.NumPaid == 1 || bin.NumPaid == 0 && bin.NumFree == 1 { // match number of adjective immediately before noun
|
||||||
|
noun = "Produkt "
|
||||||
|
} else {
|
||||||
|
noun = "Produkte "
|
||||||
|
}
|
||||||
|
if category != "" {
|
||||||
|
preposition = `aus der Kategorie "`
|
||||||
|
quote = `" `
|
||||||
|
}
|
||||||
|
if totalEuros, totalCents, err := parsePrice(order.Total); err == nil && (totalEuros > 0 || totalCents > 0) {
|
||||||
|
var adverb, fraction string
|
||||||
|
if bin.NumPaid > 1 {
|
||||||
|
adverb = "insgesamt "
|
||||||
|
}
|
||||||
|
if totalCents > 0 {
|
||||||
|
fraction = fmt.Sprintf(",%02d", totalCents)
|
||||||
|
}
|
||||||
|
total = fmt.Sprint("für ", adverb, totalEuros, fraction, " Euro ")
|
||||||
|
}
|
||||||
|
|
||||||
|
body.WriteString(fmt.Sprint(adverb, verb, free, conjunction, paid, noun, preposition, category, quote, total, "bestellt."))
|
||||||
|
|
||||||
|
adverb = adverbs[rng.Intn(len(adverbs))]
|
||||||
}
|
}
|
||||||
|
|
||||||
message := event.MessageEventContent{
|
message := event.MessageEventContent{
|
||||||
MsgType: event.MsgText,
|
MsgType: event.MsgText,
|
||||||
Body: fmt.Sprint("\U0001f389 Es ", verb, free, conjunction, paid, noun, total, "bestellt."),
|
Body: body.String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
success := s.matrix.Broadcast(&message)
|
success := s.matrix.Broadcast(&message)
|
||||||
|
@ -159,6 +182,50 @@ func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Server) fetchCategory(organizer, event string, item uint) string {
|
||||||
|
resp, err := s.doGetRequest(s.buildURL(urlItem, organizer, event, item))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 || resp.Header.Get("Content-Type") != "application/json" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(resp.Body)
|
||||||
|
itemData := struct{
|
||||||
|
Category uint
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err = decoder.Decode(&itemData)
|
||||||
|
if err != nil || itemData.Category == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = s.doGetRequest(s.buildURL(urlItemCategory, organizer, event, itemData.Category))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 || resp.Header.Get("Content-Type") != "application/json" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder = json.NewDecoder(resp.Body)
|
||||||
|
itemCategory := struct{
|
||||||
|
Name string
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err = decoder.Decode(&itemCategory)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemCategory.Name
|
||||||
|
}
|
||||||
|
|
||||||
func parsePrice(price string) (euros uint64, cents uint64, err error) {
|
func parsePrice(price string) (euros uint64, cents uint64, err error) {
|
||||||
parts := strings.Split(price, ".")
|
parts := strings.Split(price, ".")
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,9 @@ package pretix
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"git.luj0ga.de/franconian/matrix"
|
"git.luj0ga.de/franconian/matrix"
|
||||||
|
@ -96,11 +94,6 @@ func (s Server) createTables() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Server) buildURL(endpoint string, args ...any) string {
|
|
||||||
baseURL := strings.TrimSuffix(s.config.BaseURL, "/")
|
|
||||||
return fmt.Sprintf(endpoint, append([]any{baseURL}, args...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeStatus(w http.ResponseWriter, code int) {
|
func writeStatus(w http.ResponseWriter, code int) {
|
||||||
w.WriteHeader(code)
|
w.WriteHeader(code)
|
||||||
io.WriteString(w, http.StatusText(code))
|
io.WriteString(w, http.StatusText(code))
|
||||||
|
|
Loading…
Reference in New Issue