@@ -11,7 +11,9 @@ import (
1111 "time"
1212
1313 "git.handmade.network/hmn/hmn/src/config"
14+ "git.handmade.network/hmn/hmn/src/hmndata"
1415 "git.handmade.network/hmn/hmn/src/hmnurl"
16+ "git.handmade.network/hmn/hmn/src/models"
1517 "git.handmade.network/hmn/hmn/src/oops"
1618 "git.handmade.network/hmn/hmn/src/perf"
1719 "git.handmade.network/hmn/hmn/src/templates"
@@ -165,36 +167,72 @@ func SendTimeMachineEmail(profileUrl, username, userEmail, discordUsername strin
165167 return nil
166168}
167169
170+ func SendTicketPurchaseEmail (toAddress string , toName string , ticket * models.Ticket ) error {
171+ event , ok := hmndata .FindTicketEventBySlug (ticket .EventSlug )
172+ if ! ok {
173+ return oops .New (nil , "failed to find event for ticket in email" )
174+ }
175+
176+ type TicketPurchaseEmailData struct {
177+ EventName string
178+ Name string
179+ Email string
180+ CodeURL string
181+ TicketURL string
182+ }
183+ contents , err := renderTemplate ("email_ticket_purchase.html" , TicketPurchaseEmailData {
184+ EventName : event .Name ,
185+ Name : ticket .OwnerName ,
186+ Email : ticket .OwnerEmail ,
187+ CodeURL : hmnurl .BuildTicketQRCode (ticket .ID .String ()),
188+ TicketURL : hmnurl .BuildTicketSingle (ticket .ID .String ()),
189+ })
190+ if err != nil {
191+ return err
192+ }
193+
194+ err = sendMail (toAddress , toName , fmt .Sprintf ("Your ticket for the %s" , event .Name ), contents )
195+ if err != nil {
196+ return oops .New (err , "Failed to send email" )
197+ }
198+
199+ return nil
200+ }
201+
168202var EmailRegex = regexp .MustCompile (`^[^:\p{Cc} ]+@[^:\p{Cc} ]+\.[^:\p{Cc} ]+$` )
169203
170204func IsEmail (address string ) bool {
171205 return EmailRegex .Match ([]byte (address ))
172206}
173207
174- func renderTemplate (name string , data any ) (string , error ) {
208+ func renderTemplate (name string , data any ) ([] byte , error ) {
175209 var buffer bytes.Buffer
176210 template , hasTemplate := templates .GetTemplate (name )
177211 if ! hasTemplate {
178- return "" , oops .New (nil , "template not found: %s" , name )
212+ return nil , oops .New (nil , "template not found: %s" , name )
179213 }
180214 err := template .Execute (& buffer , data )
181215 if err != nil {
182- return "" , oops .New (err , "Failed to render template for email" )
216+ return nil , oops .New (err , "Failed to render template for email" )
183217 }
184- contentString := string ( buffer .Bytes () )
185- contentString = strings .ReplaceAll (contentString , "\n " , "\r \n " )
218+ contentString := buffer .Bytes ()
219+ contentString = bytes .ReplaceAll (contentString , [] byte ( "\n " ), [] byte ( "\r \n " ) )
186220 return contentString , nil
187221}
188222
189- func sendMail (toAddress , toName , subject , contentHtml string ) error {
223+ func sendMail (toAddress , toName , subject string , contentHTML [] byte ) error {
190224 if config .Config .Email .ForceToAddress != "" {
191225 toAddress = config .Config .Email .ForceToAddress
192226 }
227+ processedHTML , err := preprocessEmailHTML (contentHTML )
228+ if err != nil {
229+ return err
230+ }
193231 contents := prepMailContents (
194232 makeHeaderAddress (toAddress , toName ),
195233 makeHeaderAddress (config .Config .Email .FromAddress , config .Config .Email .FromName ),
196234 subject ,
197- contentHtml ,
235+ processedHTML ,
198236 )
199237 return smtp .SendMail (
200238 fmt .Sprintf ("%s:%d" , config .Config .Email .ServerAddress , config .Config .Email .ServerPort ),
@@ -218,18 +256,18 @@ func makeHeaderAddress(email, fullname string) string {
218256 }
219257}
220258
221- func prepMailContents (toLine string , fromLine string , subject string , contentHtml string ) []byte {
259+ func prepMailContents (toLine string , fromLine string , subject string , contentHtml [] byte ) []byte {
222260 var builder strings.Builder
223261
224- builder . WriteString ( fmt .Sprintf ( "To: %s\r \n " , toLine ) )
225- builder . WriteString ( fmt .Sprintf ( "From: %s\r \n " , fromLine ) )
226- builder . WriteString ( fmt .Sprintf ( "Date: %s\r \n " , time .Now ().UTC ().Format (time .RFC1123Z ) ))
227- builder . WriteString ( fmt .Sprintf ( "Subject: %s\r \n " , mime .QEncoding .Encode ("utf-8" , subject ) ))
262+ fmt .Fprintf ( & builder , "To: %s\r \n " , toLine )
263+ fmt .Fprintf ( & builder , "From: %s\r \n " , fromLine )
264+ fmt .Fprintf ( & builder , "Date: %s\r \n " , time .Now ().UTC ().Format (time .RFC1123Z ))
265+ fmt .Fprintf ( & builder , "Subject: %s\r \n " , mime .QEncoding .Encode ("utf-8" , subject ))
228266 builder .WriteString ("Content-Type: text/html; charset=UTF-8\r \n " )
229267 builder .WriteString ("Content-Transfer-Encoding: quoted-printable\r \n " )
230268 builder .WriteString ("\r \n " )
231269 writer := quotedprintable .NewWriter (& builder )
232- writer .Write ([] byte ( contentHtml ) )
270+ writer .Write (contentHtml )
233271 writer .Close ()
234272 builder .WriteString ("\r \n " )
235273
0 commit comments