package services import ( "bytes" "fmt" "image/color" "image/png" "strings" "time" "github.com/skip2/go-qrcode" ) // ContactData estructura para la vCard extendida type ContactData struct { FirstName string `json:"first_name"` LastName string `json:"last_name"` Nickname string `json:"nickname"` Phone string `json:"phone"` Email string `json:"email"` Org string `json:"org"` Title string `json:"title"` Role string `json:"role"` Website string `json:"website"` PhotoURL string `json:"photo_url"` Address string `json:"address"` // Ejemplo: ";;Calle 123;Ciudad;Estado;12345;País" Birthday string `json:"birthday"` // Formato: "1990-01-01" Gender string `json:"gender"` // "M", "F", "O", etc. UID string `json:"uid"` // UUID o identificador único Categories string `json:"categories"` // Ejemplo: "Amigos,Trabajo" Note string `json:"note"` IMPP string `json:"impp"` // Ejemplo: "aim:usuario" Timezone string `json:"timezone"` // Ejemplo: "-0500" Language string `json:"language"` // Ejemplo: "es" ColorHex string `json:"color"` BgColor string `json:"bg_color"` Instagram string `json:"instagram"` Facebook string `json:"facebook"` TikTok string `json:"tiktok"` LinkedIn string `json:"linkedin"` VcardID string `json:"vcard_id"` } // hexToRGBA convierte "#rrggbb" a color.RGBA func hexToRGBA(hex string) color.RGBA { var r, g, b uint8 fmt.Sscanf(hex, "#%02x%02x%02x", &r, &g, &b) return color.RGBA{r, g, b, 255} } // GenerateVCardQR genera un QR con una vCard extendida incluyendo redes sociales func GenerateVCardQR(data ContactData) ([]byte, error) { now := time.Now().UTC().Format("2006-01-02T15:04:05Z") // Usamos strings.Builder para construir dinámicamente la vCard var vcardBuilder strings.Builder vcardBuilder.WriteString("BEGIN:VCARD\n") vcardBuilder.WriteString("VERSION:3.0\n") // N y FN son obligatorios para una vCard válida vcardBuilder.WriteString(fmt.Sprintf("N:%s;%s;;;\n", data.LastName, data.FirstName)) vcardBuilder.WriteString(fmt.Sprintf("FN:%s %s\n", data.FirstName, data.LastName)) if data.Nickname != "" { vcardBuilder.WriteString(fmt.Sprintf("NICKNAME:%s\n", data.Nickname)) } if data.Org != "" { vcardBuilder.WriteString(fmt.Sprintf("ORG:%s\n", data.Org)) } if data.Title != "" { vcardBuilder.WriteString(fmt.Sprintf("TITLE:%s\n", data.Title)) } if data.Role != "" { vcardBuilder.WriteString(fmt.Sprintf("ROLE:%s\n", data.Role)) } if data.Phone != "" { vcardBuilder.WriteString(fmt.Sprintf("TEL;TYPE=CELL:%s\n", data.Phone)) } if data.Email != "" { vcardBuilder.WriteString(fmt.Sprintf("EMAIL:%s\n", data.Email)) } if data.Website != "" { vcardBuilder.WriteString(fmt.Sprintf("URL:%s\n", data.Website)) } if data.PhotoURL != "" { vcardBuilder.WriteString(fmt.Sprintf("PHOTO;VALUE=URI:%s\n", data.PhotoURL)) } if data.Address != "" { vcardBuilder.WriteString(fmt.Sprintf("ADR:%s\n", data.Address)) } if data.Birthday != "" { vcardBuilder.WriteString(fmt.Sprintf("BDAY:%s\n", data.Birthday)) } if data.Gender != "" { vcardBuilder.WriteString(fmt.Sprintf("GENDER:%s\n", data.Gender)) } if data.UID != "" { vcardBuilder.WriteString(fmt.Sprintf("UID:%s\n", data.UID)) } if data.Note != "" { vcardBuilder.WriteString(fmt.Sprintf("NOTE:%s\n", data.Note)) } if data.Categories != "" { vcardBuilder.WriteString(fmt.Sprintf("CATEGORIES:%s\n", data.Categories)) } if data.IMPP != "" { vcardBuilder.WriteString(fmt.Sprintf("IMPP:%s\n", data.IMPP)) } if data.Timezone != "" { vcardBuilder.WriteString(fmt.Sprintf("TZ:%s\n", data.Timezone)) } if data.Language != "" { vcardBuilder.WriteString(fmt.Sprintf("LANG:%s\n", data.Language)) } // Agregando las redes sociales if data.Instagram != "" { vcardBuilder.WriteString(fmt.Sprintf("X-INSTAGRAM:%s\n", data.Instagram)) } if data.Facebook != "" { vcardBuilder.WriteString(fmt.Sprintf("X-FACEBOOK:%s\n", data.Facebook)) } if data.TikTok != "" { vcardBuilder.WriteString(fmt.Sprintf("X-TIKTOK:%s\n", data.TikTok)) } if data.LinkedIn != "" { vcardBuilder.WriteString(fmt.Sprintf("X-LINKEDIN:%s\n", data.LinkedIn)) } vcardBuilder.WriteString(fmt.Sprintf("REV:%s\n", now)) vcardBuilder.WriteString("END:VCARD") vcard := vcardBuilder.String() qr, err := qrcode.New(vcard, qrcode.Medium) if err != nil { return nil, err } qr.ForegroundColor = hexToRGBA(data.ColorHex) qr.BackgroundColor = hexToRGBA(data.BgColor) img := qr.Image(300) var buf bytes.Buffer if err := png.Encode(&buf, img); err != nil { return nil, err } return buf.Bytes(), nil } func GenerateURlQR(url string) ([]byte, error) { qr, err := qrcode.New(url, qrcode.Medium) if err != nil { return nil, err } img := qr.Image(300) var buf bytes.Buffer if err := png.Encode(&buf, img); err != nil { return nil, err } return buf.Bytes(), nil }