Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Complete MVP implementation of the Cast GHL Conversation Provider bridge: - Go module setup with chi router and mongo-driver dependencies - Config loading with env var validation and defaults - MongoDB token store with upsert, get, update, delete operations - Cast.ph SMS client with 429 retry logic and typed errors - Phone number normalization (E.164 ↔ Philippine local format) - GHL OAuth 2.0 install/callback/refresh flow - GHL webhook handler with ECDSA signature verification (async dispatch) - GHL API client for message status updates and inbound message stubs - Multi-stage Dockerfile, docker-compose with MongoDB, Woodpecker CI pipeline - Unit tests for phone normalization, Cast client, GHL webhook, and OAuth handlers Co-Authored-By: SideKx <sidekx.ai@sds.dev>
71 lines
1.9 KiB
Go
71 lines
1.9 KiB
Go
package phone
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var nonDigit = regexp.MustCompile(`[^\d]`)
|
|
|
|
// ToLocal converts a phone number to Philippine local format (09XXXXXXXXX).
|
|
func ToLocal(e164 string) (string, error) {
|
|
if e164 == "" {
|
|
return "", errors.New("invalid Philippine phone number: empty input")
|
|
}
|
|
digits := nonDigit.ReplaceAllString(e164, "")
|
|
if digits == "" {
|
|
return "", errors.New("invalid Philippine phone number: no digits found")
|
|
}
|
|
|
|
var local string
|
|
switch {
|
|
case strings.HasPrefix(digits, "63") && len(digits) == 12:
|
|
local = "0" + digits[2:]
|
|
case strings.HasPrefix(digits, "9") && len(digits) == 10:
|
|
local = "0" + digits
|
|
case strings.HasPrefix(digits, "0") && len(digits) == 11:
|
|
local = digits
|
|
default:
|
|
return "", errors.New("invalid Philippine phone number")
|
|
}
|
|
|
|
if len(local) != 11 || !strings.HasPrefix(local, "09") {
|
|
return "", errors.New("invalid Philippine phone number")
|
|
}
|
|
return local, nil
|
|
}
|
|
|
|
// ToE164 converts a phone number to E.164 format (+63XXXXXXXXXX).
|
|
func ToE164(local string) (string, error) {
|
|
if local == "" {
|
|
return "", errors.New("invalid Philippine phone number: empty input")
|
|
}
|
|
|
|
// preserve leading + before stripping
|
|
hasPlus := strings.HasPrefix(local, "+")
|
|
digits := nonDigit.ReplaceAllString(local, "")
|
|
if digits == "" {
|
|
return "", errors.New("invalid Philippine phone number: no digits found")
|
|
}
|
|
|
|
var e164 string
|
|
switch {
|
|
case hasPlus && strings.HasPrefix(digits, "63") && len(digits) == 12:
|
|
e164 = "+" + digits
|
|
case !hasPlus && strings.HasPrefix(digits, "63") && len(digits) == 12:
|
|
e164 = "+" + digits
|
|
case strings.HasPrefix(digits, "0") && len(digits) == 11:
|
|
e164 = "+63" + digits[1:]
|
|
case strings.HasPrefix(digits, "9") && len(digits) == 10:
|
|
e164 = "+63" + digits
|
|
default:
|
|
return "", errors.New("invalid Philippine phone number")
|
|
}
|
|
|
|
if !regexp.MustCompile(`^\+63\d{10}$`).MatchString(e164) {
|
|
return "", errors.New("invalid Philippine phone number")
|
|
}
|
|
return e164, nil
|
|
}
|