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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -15,8 +16,17 @@ const (
|
|||
eventOrderPlaced = "pretix.event.order.placed"
|
||||
|
||||
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) {
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
writeStatus(w, http.StatusBadRequest)
|
||||
|
@ -42,16 +52,7 @@ func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, s.buildURL(urlIndividualOrder, data.Organizer, data.Event, data.Code), nil)
|
||||
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)
|
||||
resp, err := s.doGetRequest(s.buildURL(urlIndividualOrder, data.Organizer, data.Event, data.Code))
|
||||
if err != nil {
|
||||
writeStatus(w, http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -73,6 +74,7 @@ func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
|||
Code string
|
||||
Datetime time.Time
|
||||
Positions []struct{
|
||||
Item uint
|
||||
Price 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
|
||||
numFree := 0
|
||||
numPaid := 0
|
||||
categories := map[string]categoryBin{}
|
||||
|
||||
for _, position := range order.Positions {
|
||||
euros, cents, err := parsePrice(position.Price)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
name := s.fetchCategory(data.Organizer, data.Event, position.Item)
|
||||
|
||||
category := categories[name]
|
||||
if euros > 0 || cents > 0 {
|
||||
numPaid++
|
||||
category.NumPaid++
|
||||
} else {
|
||||
numFree++
|
||||
category.NumFree++
|
||||
}
|
||||
|
||||
categories[name] = category
|
||||
}
|
||||
|
||||
var verb, free, conjunction, paid, noun, total string
|
||||
if numFree+numPaid == 1 {
|
||||
verb = "wurde "
|
||||
} else {
|
||||
verb = "wurden "
|
||||
}
|
||||
if numFree == 1 {
|
||||
free = "ein kostenloses "
|
||||
} else if numFree > 1 {
|
||||
free = fmt.Sprint(numFree, " kostenlose ")
|
||||
}
|
||||
if numPaid == 1 {
|
||||
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 "
|
||||
var body strings.Builder
|
||||
body.WriteString("\U0001f389")
|
||||
|
||||
rng := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
|
||||
adverb := " Es "
|
||||
for category, bin := range categories {
|
||||
var verb, free, conjunction, paid, noun, preposition, quote, total string
|
||||
if bin.NumFree+bin.NumPaid == 1 {
|
||||
verb = "wurde "
|
||||
} else {
|
||||
verb = "wurden "
|
||||
}
|
||||
if totalCents > 0 {
|
||||
fraction = fmt.Sprintf(",%02d", totalCents)
|
||||
if bin.NumFree == 1 {
|
||||
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{
|
||||
MsgType: event.MsgText,
|
||||
Body: fmt.Sprint("\U0001f389 Es ", verb, free, conjunction, paid, noun, total, "bestellt."),
|
||||
Body: body.String(),
|
||||
}
|
||||
|
||||
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) {
|
||||
parts := strings.Split(price, ".")
|
||||
|
||||
|
|
|
@ -3,11 +3,9 @@ package pretix
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.luj0ga.de/franconian/matrix"
|
||||
|
@ -96,11 +94,6 @@ func (s Server) createTables() error {
|
|||
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) {
|
||||
w.WriteHeader(code)
|
||||
io.WriteString(w, http.StatusText(code))
|
||||
|
|
Loading…
Reference in New Issue