diff --git a/backends/rapidpro/backend_test.go b/backends/rapidpro/backend_test.go index 1ababf41b..a22ee3dea 100644 --- a/backends/rapidpro/backend_test.go +++ b/backends/rapidpro/backend_test.go @@ -23,6 +23,7 @@ import ( "github.com/nyaruka/courier/test" "github.com/nyaruka/gocommon/dbutil/assertdb" "github.com/nyaruka/gocommon/httpx" + "github.com/nyaruka/gocommon/i18n" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/gocommon/urns" "github.com/nyaruka/gocommon/uuids" @@ -195,7 +196,7 @@ func (ts *BackendTestSuite) TestDeleteMsgByExternalID() { func (ts *BackendTestSuite) TestContact() { knChannel := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c95d") clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, knChannel, nil) - urn, _ := urns.NewTelURNForCountry("12065551518", "US") + urn := urns.URN("tel:+12065551518") ctx := context.Background() now := time.Now() @@ -220,7 +221,7 @@ func (ts *BackendTestSuite) TestContact() { ts.True(contact2.CreatedOn_.Before(now2)) // load a contact by URN instead (this one is in our testdata) - cURN, _ := urns.NewTelURNForCountry("+12067799192", "US") + cURN := urns.URN("tel:+12067799192") contact, err = contactForURN(ctx, ts.b, knChannel.OrgID(), knChannel, cURN, nil, "", clog) ts.NoError(err) ts.NotNil(contact) @@ -228,7 +229,7 @@ func (ts *BackendTestSuite) TestContact() { ts.Equal(null.String(""), contact.Name_) ts.Equal(courier.ContactUUID("a984069d-0008-4d8c-a772-b14a8a6acccc"), contact.UUID_) - urn, _ = urns.NewTelURNForCountry("12065551519", "US") + urn = urns.URN("tel:+12065551519") // long name are truncated @@ -243,7 +244,7 @@ func (ts *BackendTestSuite) TestContact() { func (ts *BackendTestSuite) TestContactRace() { knChannel := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c95d") clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, knChannel, nil) - urn, _ := urns.NewTelURNForCountry("12065551518", "US") + urn := urns.URN("tel:+12065551518") urnSleep = true defer func() { urnSleep = false }() @@ -273,8 +274,7 @@ func (ts *BackendTestSuite) TestAddAndRemoveContactURN() { clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, knChannel, nil) ctx := context.Background() - cURN, err := urns.NewTelURNForCountry("+12067799192", "US") - ts.NoError(err) + cURN := urns.URN("tel:+12067799192") contact, err := contactForURN(ctx, ts.b, knChannel.OrgID_, knChannel, cURN, nil, "", clog) ts.NoError(err) @@ -287,7 +287,7 @@ func (ts *BackendTestSuite) TestAddAndRemoveContactURN() { ts.NoError(err) ts.Equal(len(contactURNs), 1) - urn, _ := urns.NewTelURNForCountry("12065551518", "US") + urn := urns.URN("tel:+12065551518") addedURN, err := ts.b.AddURNtoContact(ctx, knChannel, contact, urn, nil) ts.NoError(err) ts.NotNil(addedURN) @@ -314,7 +314,7 @@ func (ts *BackendTestSuite) TestContactURN() { knChannel := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c95d") twChannel := ts.getChannel("TW", "dbc126ed-66bc-4e28-b67b-81dc3327c96a") clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, knChannel, nil) - urn, _ := urns.NewTelURNForCountry("12065551515", "US") + urn := urns.URN("tel:+12065551515") ctx := context.Background() @@ -371,12 +371,12 @@ func (ts *BackendTestSuite) TestContactURN() { // test that we don't use display when looking up URNs tgChannel := ts.getChannel("TG", "dbc126ed-66bc-4e28-b67b-81dc3327c98a") - tgURN, _ := urns.NewTelegramURN(12345, "") + tgURN := urns.URN("telegram:12345") tgContact, err := contactForURN(ctx, ts.b, tgChannel.OrgID_, tgChannel, tgURN, nil, "", clog) ts.NoError(err) - tgURNDisplay, _ := urns.NewTelegramURN(12345, "Jane") + tgURNDisplay := urns.URN("telegram:12345#Jane") displayContact, err := contactForURN(ctx, ts.b, tgChannel.OrgID_, tgChannel, tgURNDisplay, nil, "", clog) ts.NoError(err) @@ -393,7 +393,7 @@ func (ts *BackendTestSuite) TestContactURN() { ts.Equal(null.String("Jane"), tgContactURN.Display) // try to create two contacts at the same time in goroutines, this tests our transaction rollbacks - urn2, _ := urns.NewTelURNForCountry("12065551616", "US") + urn2 := urns.URN("tel:+12065551616") var wait sync.WaitGroup var contact2, contact3 *Contact wait.Add(2) @@ -419,8 +419,8 @@ func (ts *BackendTestSuite) TestContactURN() { func (ts *BackendTestSuite) TestContactURNPriority() { knChannel := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c95d") twChannel := ts.getChannel("TW", "dbc126ed-66bc-4e28-b67b-81dc3327c96a") - knURN, _ := urns.NewTelURNForCountry("12065551111", "US") - twURN, _ := urns.NewTelURNForCountry("12065552222", "US") + knURN := urns.URN("tel:+12065551111") + twURN := urns.URN("tel:+12065552222") clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, knChannel, nil) ctx := context.Background() @@ -627,12 +627,12 @@ func (ts *BackendTestSuite) TestMsgStatus() { // update URN when the new doesn't exist tx, _ := ts.b.db.BeginTxx(ctx, nil) - oldURN, _ := urns.NewWhatsAppURN("55988776655") + oldURN := urns.URN("whatsapp:55988776655") _ = insertContactURN(tx, newContactURN(channel.OrgID_, channel.ID_, NilContactID, oldURN, nil)) ts.NoError(tx.Commit()) - newURN, _ := urns.NewWhatsAppURN("5588776655") + newURN := urns.URN("whatsapp:5588776655") status = ts.b.NewStatusUpdate(channel, courier.MsgID(10000), courier.MsgStatusSent, clog6) status.SetURNUpdate(oldURN, newURN) @@ -646,8 +646,8 @@ func (ts *BackendTestSuite) TestMsgStatus() { ts.NoError(tx.Commit()) // new URN already exits but don't have an associated contact - oldURN, _ = urns.NewWhatsAppURN("55999887766") - newURN, _ = urns.NewWhatsAppURN("5599887766") + oldURN = urns.URN("whatsapp:55999887766") + newURN = urns.URN("whatsapp:5599887766") tx, _ = ts.b.db.BeginTxx(ctx, nil) contact, _ := contactForURN(ctx, ts.b, channel.OrgID_, channel, oldURN, nil, "", clog6) _ = insertContactURN(tx, newContactURN(channel.OrgID_, channel.ID_, NilContactID, newURN, nil)) @@ -668,8 +668,8 @@ func (ts *BackendTestSuite) TestMsgStatus() { ts.NoError(tx.Commit()) // new URN already exits and have an associated contact - oldURN, _ = urns.NewWhatsAppURN("55988776655") - newURN, _ = urns.NewWhatsAppURN("5588776655") + oldURN = urns.URN("whatsapp:55988776655") + newURN = urns.URN("whatsapp:5588776655") tx, _ = ts.b.db.BeginTxx(ctx, nil) _, _ = contactForURN(ctx, ts.b, channel.OrgID_, channel, oldURN, nil, "", clog6) otherContact, _ := contactForURN(ctx, ts.b, channel.OrgID_, channel, newURN, nil, "", clog6) @@ -747,8 +747,8 @@ func (ts *BackendTestSuite) TestCheckForDuplicate() { ctx := context.Background() knChannel := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c95d") twChannel := ts.getChannel("TW", "dbc126ed-66bc-4e28-b67b-81dc3327c96a") - urn, _ := urns.NewTelURNForCountry("12065551215", knChannel.Country()) - urn2, _ := urns.NewTelURNForCountry("12065551277", knChannel.Country()) + urn := urns.URN("tel:+12065551215") + urn2 := urns.URN("tel:+12065551277") createAndWriteMsg := func(ch courier.Channel, u urns.URN, text, extID string) *Msg { clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, knChannel, nil) @@ -900,14 +900,14 @@ func (ts *BackendTestSuite) TestOutgoingQueue() { func (ts *BackendTestSuite) TestChannel() { noAddress := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c99a") - ts.Equal("US", noAddress.Country()) + ts.Equal(i18n.Country("US"), noAddress.Country()) ts.Equal(courier.NilChannelAddress, noAddress.ChannelAddress()) knChannel := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c95d") ts.Equal("2500", knChannel.Address()) ts.Equal(courier.ChannelAddress("2500"), knChannel.ChannelAddress()) - ts.Equal("RW", knChannel.Country()) + ts.Equal(i18n.Country("RW"), knChannel.Country()) ts.Equal([]courier.ChannelRole{courier.ChannelRoleSend, courier.ChannelRoleReceive}, knChannel.Roles()) ts.True(knChannel.HasRole(courier.ChannelRoleSend)) ts.True(knChannel.HasRole(courier.ChannelRoleReceive)) @@ -1098,7 +1098,7 @@ func (ts *BackendTestSuite) TestWriteMsg() { now := time.Now().Round(time.Microsecond).In(time.UTC) // create a new courier msg - urn, _ := urns.NewTelURNForCountry("12065551212", knChannel.Country()) + urn := urns.URN("tel:+12065551212") msg := ts.b.NewIncomingMsg(knChannel, urn, "test123", "ext123", clog).WithReceivedOn(now).WithContactName("test contact").(*Msg) // try to write it to our db @@ -1197,7 +1197,7 @@ func (ts *BackendTestSuite) TestWriteMsgWithAttachments() { knChannel := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c95d") clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, knChannel, nil) - urn, _ := urns.NewTelURNForCountry("12065551218", knChannel.Country()) + urn := urns.URN("tel:+12065551218") msg := ts.b.NewIncomingMsg(knChannel, urn, "two regular attachments", "", clog).(*Msg) msg.WithAttachment("http://example.com/test.jpg") @@ -1242,7 +1242,7 @@ func (ts *BackendTestSuite) TestPreferredChannelCheckRole() { // have to round to microseconds because postgres can't store nanos now := time.Now().Round(time.Microsecond).In(time.UTC) - urn, _ := urns.NewTelURNForCountry("12065552020", exChannel.Country()) + urn := urns.URN("tel:+12065552020") msg := ts.b.NewIncomingMsg(exChannel, urn, "test123", "ext123", clog).WithReceivedOn(now).WithContactName("test contact").(*Msg) // try to write it to our db @@ -1270,7 +1270,7 @@ func (ts *BackendTestSuite) TestChannelEvent() { ctx := context.Background() channel := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c95d") clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, channel, nil) - urn, _ := urns.NewTelURNForCountry("12065551616", channel.Country()) + urn := urns.URN("tel:+12065551616") event := ts.b.NewChannelEvent(channel, courier.EventTypeReferral, urn, clog).WithExtra(map[string]string{"ref_id": "12345"}).WithContactName("kermit frog") err := ts.b.WriteChannelEvent(ctx, event, clog) @@ -1319,7 +1319,7 @@ func (ts *BackendTestSuite) TestMailroomEvents() { channel := ts.getChannel("KN", "dbc126ed-66bc-4e28-b67b-81dc3327c95d") clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, channel, nil) - urn, _ := urns.NewTelURNForCountry("12065551616", channel.Country()) + urn := urns.URN("tel:+12065551616") event := ts.b.NewChannelEvent(channel, courier.EventTypeReferral, urn, clog). WithExtra(map[string]string{"ref_id": "12345"}). diff --git a/backends/rapidpro/channel.go b/backends/rapidpro/channel.go index a99bc2dc5..59b7737d5 100644 --- a/backends/rapidpro/channel.go +++ b/backends/rapidpro/channel.go @@ -8,6 +8,8 @@ import ( "github.com/lib/pq" "github.com/nyaruka/courier" + "github.com/nyaruka/gocommon/i18n" + "github.com/nyaruka/gocommon/urns" "github.com/nyaruka/null/v3" ) @@ -56,11 +58,11 @@ func (c *Channel) ChannelAddress() courier.ChannelAddress { } // Country returns the country code for this channel if any -func (c *Channel) Country() string { return c.Country_.String } +func (c *Channel) Country() i18n.Country { return i18n.Country(c.Country_.String) } // IsScheme returns whether this channel serves only the passed in scheme -func (c *Channel) IsScheme(scheme string) bool { - return len(c.Schemes_) == 1 && c.Schemes_[0] == scheme +func (c *Channel) IsScheme(scheme *urns.Scheme) bool { + return len(c.Schemes_) == 1 && c.Schemes_[0] == scheme.Prefix } // Roles returns the roles of this channel diff --git a/backends/rapidpro/urn.go b/backends/rapidpro/urn.go index 07717754d..b57ced17c 100644 --- a/backends/rapidpro/urn.go +++ b/backends/rapidpro/urn.go @@ -145,7 +145,7 @@ func setDefaultURN(db *sqlx.Tx, channel *Channel, contact *Contact, urn urns.URN existing.Priority = currPriority // if this is a phone number and we just received a message on a tel scheme, set that as our new preferred channel - if existing.Scheme == urns.TelScheme && scheme == urns.TelScheme && channel.HasRole(courier.ChannelRoleSend) { + if existing.Scheme == urns.Phone.Prefix && scheme == urns.Phone.Prefix && channel.HasRole(courier.ChannelRoleSend) { existing.ChannelID = channel.ID() } currPriority-- diff --git a/channel.go b/channel.go index a8bbf19fd..a63111510 100644 --- a/channel.go +++ b/channel.go @@ -4,6 +4,8 @@ import ( "database/sql/driver" "errors" + "github.com/nyaruka/gocommon/i18n" + "github.com/nyaruka/gocommon/urns" "github.com/nyaruka/gocommon/uuids" "github.com/nyaruka/null/v3" ) @@ -123,14 +125,14 @@ type Channel interface { Name() string ChannelType() ChannelType Schemes() []string - Country() string + Country() i18n.Country Address() string ChannelAddress() ChannelAddress Roles() []ChannelRole // is this channel for the passed in scheme (and only that scheme) - IsScheme(string) bool + IsScheme(*urns.Scheme) bool // CallbackDomain returns the domain that should be used for any callbacks the channel registers CallbackDomain(fallbackDomain string) string diff --git a/go.mod b/go.mod index 2f8174580..493bfe001 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22 require ( github.com/antchfx/xmlquery v1.4.0 - github.com/aws/aws-sdk-go v1.51.25 + github.com/aws/aws-sdk-go v1.52.3 github.com/buger/jsonparser v1.1.1 github.com/dghubble/oauth1 v0.7.3 github.com/getsentry/sentry-go v0.27.0 @@ -13,10 +13,10 @@ require ( github.com/gomodule/redigo v1.9.2 github.com/gorilla/schema v1.3.0 github.com/h2non/filetype v1.1.3 - github.com/jmoiron/sqlx v1.3.5 + github.com/jmoiron/sqlx v1.4.0 github.com/lib/pq v1.10.9 github.com/nyaruka/ezconf v0.3.0 - github.com/nyaruka/gocommon v1.53.2 + github.com/nyaruka/gocommon v1.54.2 github.com/nyaruka/null/v3 v3.0.0 github.com/nyaruka/redisx v0.8.0 github.com/patrickmn/go-cache v2.1.0+incompatible @@ -24,7 +24,7 @@ require ( github.com/samber/slog-multi v1.0.2 github.com/samber/slog-sentry v1.2.2 github.com/stretchr/testify v1.9.0 - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/mod v0.17.0 gopkg.in/go-playground/validator.v9 v9.31.0 ) @@ -36,7 +36,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.19.0 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/gorilla/websocket v1.5.1 // indirect @@ -52,12 +52,12 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/samber/lo v1.39.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/net v0.24.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index bcacb33e8..f26cfb980 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,11 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/antchfx/xmlquery v1.4.0 h1:xg2HkfcRK2TeTbdb0m1jxCYnvsPaGY/oeZWTGqX/0hA= github.com/antchfx/xmlquery v1.4.0/go.mod h1:Ax2aeaeDjfIw3CwXKDQ0GkwZ6QlxoChlIBP+mGnDFjI= github.com/antchfx/xpath v1.3.0 h1:nTMlzGAK3IJ0bPpME2urTuFL76o4A96iYvoKFHRXJgc= github.com/antchfx/xpath v1.3.0/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= -github.com/aws/aws-sdk-go v1.51.25 h1:DjTT8mtmsachhV6yrXR8+yhnG6120dazr720nopRsls= -github.com/aws/aws-sdk-go v1.51.25/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.52.3 h1:BNPJmHOXNoM/iBWJKrvaQvJOweRcp3KLpzdb65CfQwU= +github.com/aws/aws-sdk-go v1.52.3/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -27,10 +29,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= -github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= @@ -39,8 +41,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s= github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/schema v1.3.0 h1:rbciOzXAx3IB8stEFnfTwO3sYa6EWlQk79XdyustPDA= github.com/gorilla/schema v1.3.0/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= @@ -53,25 +55,24 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.1 h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8= github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nyaruka/ezconf v0.3.0 h1:kGvJqVN8AHowb4HdaHAviJ0Z3yI5Pyekp1WqibFEaGk= github.com/nyaruka/ezconf v0.3.0/go.mod h1:89GUW6EPRNLIxT7lC4LWnjWTgZeQwRoX7lBmc8ralAU= -github.com/nyaruka/gocommon v1.53.2 h1:DKXlvNUcim/+4X0Dz8BjdDHmQBNVU60giEOVu2faRCE= -github.com/nyaruka/gocommon v1.53.2/go.mod h1:1E8KKcJ4r+FPWQm5ImGjiH6K96dKInjLM9BkcD4zOUQ= +github.com/nyaruka/gocommon v1.54.2 h1:bJngg/Dhc6x01vq9lNMVc5YAh/nsAItd/xzbT83bASc= +github.com/nyaruka/gocommon v1.54.2/go.mod h1:3rM5QdBU5YzFn/3KlDI0skPEghgssIS4WtLiji99JsA= github.com/nyaruka/librato v1.1.1 h1:0nTYtJLl3Sn7lX3CuHsLf+nXy1k/tGV0OjVxLy3Et4s= github.com/nyaruka/librato v1.1.1/go.mod h1:fme1Fu1PT2qvkaBZyw8WW+SrnFe2qeeCWpvqmAaKAKE= github.com/nyaruka/null/v2 v2.0.3 h1:rdmMRQyVzrOF3Jff/gpU/7BDR9mQX0lcLl4yImsA3kw= @@ -106,10 +107,10 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= @@ -117,8 +118,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= @@ -129,8 +130,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -138,14 +139,14 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= diff --git a/handlers/dart/handler.go b/handlers/dart/handler.go index a46b84d2c..5c720146a 100644 --- a/handlers/dart/handler.go +++ b/handlers/dart/handler.go @@ -77,7 +77,7 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w // create our URN urn, err := handlers.StrictTelForCountry(form.Original, channel.Country()) if err != nil { - urn, err = urns.NewURNFromParts(urns.ExternalScheme, form.Original, "", "") + urn, err = urns.New(urns.External, form.Original) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } diff --git a/handlers/dialog360/handler.go b/handlers/dialog360/handler.go index 4001e917b..c53fe2896 100644 --- a/handlers/dialog360/handler.go +++ b/handlers/dialog360/handler.go @@ -132,9 +132,9 @@ func (h *handler) processWhatsAppPayload(ctx context.Context, channel courier.Ch } date := time.Unix(ts, 0).UTC() - urn, err := urns.NewWhatsAppURN(msg.From) + urn, err := urns.New(urns.WhatsApp, msg.From) if err != nil { - return nil, nil, handlers.WriteAndLogRequestIgnored(ctx, h, channel, w, r, err.Error()) + return nil, nil, handlers.WriteAndLogRequestIgnored(ctx, h, channel, w, r, "invalid whatsapp id") } for _, msgError := range msg.Errors { diff --git a/handlers/discord/handler.go b/handlers/discord/handler.go index 19df1c856..c4e69fda1 100644 --- a/handlers/discord/handler.go +++ b/handlers/discord/handler.go @@ -90,7 +90,7 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w date := time.Now() // create our URN - urn, err := urns.NewURNFromParts(urns.DiscordScheme, from, "", "") + urn, err := urns.New(urns.Discord, from) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } diff --git a/handlers/external/handler.go b/handlers/external/handler.go index 794eea664..34407c4f6 100644 --- a/handlers/external/handler.go +++ b/handlers/external/handler.go @@ -100,15 +100,15 @@ func (h *handler) receiveStopContact(ctx context.Context, channel courier.Channe // create our URN urn := urns.NilURN - if channel.Schemes()[0] == urns.TelScheme { + if channel.Schemes()[0] == urns.Phone.Prefix { urn, err = handlers.StrictTelForCountry(form.From, channel.Country()) } else { - urn, err = urns.NewURNFromParts(channel.Schemes()[0], form.From, "", "") + urn = urns.URN(channel.Schemes()[0] + ":" + form.From) + err = urn.Validate() } if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } - urn = urn.Normalize("") // create a stop channel event channelEvent := h.Backend().NewChannelEvent(channel, courier.EventTypeStopContact, urn, clog) @@ -202,15 +202,15 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w // create our URN urn := urns.NilURN - if channel.Schemes()[0] == urns.TelScheme { + if channel.Schemes()[0] == urns.Phone.Prefix { urn, err = handlers.StrictTelForCountry(from, channel.Country()) } else { - urn, err = urns.NewURNFromParts(channel.Schemes()[0], from, "", "") + urn = urns.URN(channel.Schemes()[0] + ":" + from) + err = urn.Validate() } if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } - urn = urn.Normalize(channel.Country()) // build our msg msg := h.Backend().NewIncomingMsg(channel, urn, text, "", clog).WithReceivedOn(date) @@ -309,9 +309,9 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen // if we are meant to use national formatting (no country code) pull that out if useNational { - nationalTo := msg.URN().Localize(channel.Country()) - form["to"] = nationalTo.Path() - form["to_no_plus"] = nationalTo.Path() + nationalTo := urns.ToLocalPhone(msg.URN(), channel.Country()) + form["to"] = nationalTo + form["to_no_plus"] = nationalTo } // if we are smart, first try to convert to GSM7 chars diff --git a/handlers/facebook_legacy/handler.go b/handlers/facebook_legacy/handler.go index 95e0bb2fe..1a87fe502 100644 --- a/handlers/facebook_legacy/handler.go +++ b/handlers/facebook_legacy/handler.go @@ -252,9 +252,9 @@ func (h *handler) receiveEvents(ctx context.Context, channel courier.Channel, w date := time.Unix(0, msg.Timestamp*1000000).UTC() // create our URN - urn, err := urns.NewFacebookURN(msg.Sender.ID) + urn, err := urns.New(urns.Facebook, msg.Sender.ID) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid facebook id")) } if msg.OptIn != nil { // this is an opt in, if we have a user_ref, use that as our URN (this is a checkbox plugin) @@ -264,7 +264,7 @@ func (h *handler) receiveEvents(ctx context.Context, channel courier.Channel, w // Right now that we even support this isn't documented and I don't think anybody uses it, so leaving that out. // (things will still work, we just will have dupe contacts, one with user_ref for the first contact, then with the real id when they reply) if msg.OptIn.UserRef != "" { - urn, err = urns.NewFacebookURN(urns.FacebookRefPrefix + msg.OptIn.UserRef) + urn, err = urns.New(urns.Facebook, urns.FacebookRefPrefix+msg.OptIn.UserRef) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } @@ -485,8 +485,8 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen } // build our recipient - if msg.URN().IsFacebookRef() { - payload.Recipient.UserRef = msg.URN().FacebookRef() + if IsFacebookRef(msg.URN()) { + payload.Recipient.UserRef = FacebookRef(msg.URN()) } else { payload.Recipient.ID = msg.URN().Path() } @@ -554,15 +554,15 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen } res.AddExternalID(externalID) - if msg.URN().IsFacebookRef() { + if IsFacebookRef(msg.URN()) { recipientID, err := jsonparser.GetString(respBody, "recipient_id") if err != nil { return courier.ErrFailedWithReason("", "response missing recipient_id") } - referralID := msg.URN().FacebookRef() + referralID := FacebookRef(msg.URN()) - realIDURN, err := urns.NewFacebookURN(recipientID) + realIDURN, err := urns.New(urns.Facebook, recipientID) if err != nil { clog.RawError(errors.Errorf("unable to make facebook urn from %s", recipientID)) } @@ -575,7 +575,7 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen if err != nil { clog.RawError(errors.Errorf("unable to add real facebook URN %s to contact with uuid %s", realURN.String(), contact.UUID())) } - referralIDExtURN, err := urns.NewURNFromParts(urns.ExternalScheme, referralID, "", "") + referralIDExtURN, err := urns.New(urns.External, referralID) if err != nil { clog.RawError(errors.Errorf("unable to make ext urn from %s", referralID)) } @@ -598,7 +598,7 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen // DescribeURN looks up URN metadata for new contacts func (h *handler) DescribeURN(ctx context.Context, channel courier.Channel, urn urns.URN, clog *courier.ChannelLog) (map[string]string, error) { // can't do anything with facebook refs, ignore them - if urn.IsFacebookRef() { + if IsFacebookRef(urn) { return map[string]string{}, nil } @@ -629,3 +629,15 @@ func (h *handler) DescribeURN(ctx context.Context, channel courier.Channel, urn return map[string]string{"name": utils.JoinNonEmpty(" ", firstName, lastName)}, nil } + +func IsFacebookRef(u urns.URN) bool { + return u.Scheme() == urns.Facebook.Prefix && strings.HasPrefix(u.Path(), urns.FacebookRefPrefix) +} + +// FacebookRef returns the facebook referral portion of our path, this return empty string in the case where we aren't a Facebook scheme +func FacebookRef(u urns.URN) string { + if IsFacebookRef(u) { + return strings.TrimPrefix(u.Path(), urns.FacebookRefPrefix) + } + return "" +} diff --git a/handlers/firebase/handler.go b/handlers/firebase/handler.go index ccd329378..7ae1435b7 100644 --- a/handlers/firebase/handler.go +++ b/handlers/firebase/handler.go @@ -70,7 +70,7 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w } // create our URN - urn, err := urns.NewFirebaseURN(form.From) + urn, err := urns.New(urns.Firebase, form.From) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } @@ -103,7 +103,7 @@ func (h *handler) registerContact(ctx context.Context, channel courier.Channel, } // create our URN - urn, err := urns.NewFirebaseURN(form.URN) + urn, err := urns.New(urns.Firebase, form.URN) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } diff --git a/handlers/freshchat/handler.go b/handlers/freshchat/handler.go index 133656eb3..2ff912eb7 100644 --- a/handlers/freshchat/handler.go +++ b/handlers/freshchat/handler.go @@ -70,7 +70,7 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w // create our URN urn := urns.NilURN urnstring := fmt.Sprintf("%s/%s", payload.Data.Message.ChannelID, payload.Data.Message.ActorID) - urn, err = urns.NewURNFromParts("freshchat", urnstring, "", "") + urn, err = urns.New(urns.FreshChat, urnstring) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } diff --git a/handlers/jiochat/handler.go b/handlers/jiochat/handler.go index 963a50086..1ec5db2c3 100644 --- a/handlers/jiochat/handler.go +++ b/handlers/jiochat/handler.go @@ -115,9 +115,9 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w } date := time.Unix(payload.CreateTime/1000, payload.CreateTime%1000*1000000).UTC() - urn, err := urns.NewURNFromParts(urns.JiochatScheme, payload.FromUsername, "", "") + urn, err := urns.New(urns.JioChat, payload.FromUsername) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid jiochat id")) } // subscribe event, trigger a new conversation diff --git a/handlers/kaleyra/handler.go b/handlers/kaleyra/handler.go index a3f45f0a0..bd38bbf98 100644 --- a/handlers/kaleyra/handler.go +++ b/handlers/kaleyra/handler.go @@ -80,9 +80,9 @@ func (h *handler) receiveMsg(ctx context.Context, channel courier.Channel, w htt } // build urn - urn, err := urns.NewWhatsAppURN(form.From) + urn, err := urns.New(urns.WhatsApp, form.From) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid whatsapp id")) } // parse created_at timestamp diff --git a/handlers/kannel/handler.go b/handlers/kannel/handler.go index b71874791..7094d0d5a 100644 --- a/handlers/kannel/handler.go +++ b/handlers/kannel/handler.go @@ -11,6 +11,7 @@ import ( "github.com/nyaruka/courier" "github.com/nyaruka/courier/handlers" "github.com/nyaruka/gocommon/gsm7" + "github.com/nyaruka/gocommon/urns" ) const ( @@ -150,8 +151,7 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen // if we are meant to use national formatting (no country code) pull that out if useNational { - nationalTo := msg.URN().Localize(msg.Channel().Country()) - form["to"] = []string{nationalTo.Path()} + form["to"] = []string{urns.ToLocalPhone(msg.URN(), msg.Channel().Country())} } // figure out what encoding to tell kannel to send as diff --git a/handlers/line/handler.go b/handlers/line/handler.go index ed4866eaa..37ec83616 100644 --- a/handlers/line/handler.go +++ b/handlers/line/handler.go @@ -148,9 +148,9 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w // create our date from the timestamp (they give us millis, arg is nanos) date := time.Unix(0, lineEvent.Timestamp*1000000).UTC() - urn, err := urns.NewURNFromParts(urns.LineScheme, lineEvent.Source.UserID, "", "") + urn, err := urns.New(urns.Line, lineEvent.Source.UserID) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid line id")) } msg := h.Backend().NewIncomingMsg(channel, urn, text, lineEvent.ReplyToken, clog).WithReceivedOn(date) diff --git a/handlers/messagebird/handler.go b/handlers/messagebird/handler.go index 01127ed21..ef8eb60fa 100644 --- a/handlers/messagebird/handler.go +++ b/handlers/messagebird/handler.go @@ -125,7 +125,7 @@ func (h *handler) receiveStatus(ctx context.Context, channel courier.Channel, w } if receivedStatus.StatusErrorCode == errorStopped { - urn, err := urns.NewTelURNForCountry(receivedStatus.Recipient, "") + urn, err := urns.ParsePhone(receivedStatus.Recipient, "") if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } diff --git a/handlers/messagebird/handler_test.go b/handlers/messagebird/handler_test.go index b157c271f..600ca3c41 100644 --- a/handlers/messagebird/handler_test.go +++ b/handlers/messagebird/handler_test.go @@ -21,10 +21,10 @@ var testChannels = []courier.Channel{ const ( receiveURL = "/c/mbd/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/receive" - validReceive = `{"receiver":"18005551515","sender":"188885551515","message":"Test again","date":1690386569,"date_utc":1690418969,"reference":"1","id":"b6aae1b5dfb2427a8f7ea6a717ba31a9","message_id":"3b53c137369242138120d6b0b2122607","recipient":"18005551515","originator":"188885551515","body":"Test 3","createdDatetime":"2023-07-27T00:49:29+00:00","mms":false}` - validReceiveShortCode = `{"receiver":"18005551515","sender":"188885551515","message":"Test again","date":1690386569,"date_utc":1690418969,"reference":"1","id":"b6aae1b5dfb2427a8f7ea6a717ba31a9","message_id":"3b53c137369242138120d6b0b2122607","recipient":"18005551515","originator":"188885551515","body":"Test 3","createdDatetime":"20230727004929","mms":false}` - validReceiveMMS = `{"receiver":"18005551515","sender":"188885551515","message":"Test again","date":1690386569,"date_utc":1690418969,"reference":"1","id":"b6aae1b5dfb2427a8f7ea6a717ba31a9","message_id":"3b53c137369242138120d6b0b2122607","recipient":"18005551515","originator":"188885551515","mediaURLs":["https://foo.bar/image.jpg"],"createdDatetime":"2023-07-27T00:49:29+00:00","mms":true}` - statusBaseURL = "/c/mbd/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?datacoding=plain&id=b6aae1b5dfb2427a8f7ea6a717ba31a9&mccmnc=310010&messageLength=4&messagePartCount=1&ported=0&price%5Bamount%5D=0.000&price%5Bcurrency%5D=USD&recipient=188885551515&reference=26&statusDatetime=2023-07-28T17%3A57%3A12%2B00%3A00" + validReceive = `{"receiver":"18005551515","sender":"18885551515","message":"Test again","date":1690386569,"date_utc":1690418969,"reference":"1","id":"b6aae1b5dfb2427a8f7ea6a717ba31a9","message_id":"3b53c137369242138120d6b0b2122607","recipient":"18005551515","originator":"18885551515","body":"Test 3","createdDatetime":"2023-07-27T00:49:29+00:00","mms":false}` + validReceiveShortCode = `{"receiver":"18005551515","sender":"18885551515","message":"Test again","date":1690386569,"date_utc":1690418969,"reference":"1","id":"b6aae1b5dfb2427a8f7ea6a717ba31a9","message_id":"3b53c137369242138120d6b0b2122607","recipient":"18005551515","originator":"18885551515","body":"Test 3","createdDatetime":"20230727004929","mms":false}` + validReceiveMMS = `{"receiver":"18005551515","sender":"18885551515","message":"Test again","date":1690386569,"date_utc":1690418969,"reference":"1","id":"b6aae1b5dfb2427a8f7ea6a717ba31a9","message_id":"3b53c137369242138120d6b0b2122607","recipient":"18005551515","originator":"18885551515","mediaURLs":["https://foo.bar/image.jpg"],"createdDatetime":"2023-07-27T00:49:29+00:00","mms":true}` + statusBaseURL = "/c/mbd/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?datacoding=plain&id=b6aae1b5dfb2427a8f7ea6a717ba31a9&mccmnc=310010&messageLength=4&messagePartCount=1&ported=0&price%5Bamount%5D=0.000&price%5Bcurrency%5D=USD&recipient=18885551515&reference=26&statusDatetime=2023-07-28T17%3A57%3A12%2B00%3A00" validSecret = "my_super_secret" validResponse = `{"id":"efa6405d518d4c0c88cce11f7db775fb","href":"https://rest.messagebird.com/mms/efa6405d518d4c0c88cce11f7db775fb","direction":"mt","originator":"+188885551515","subject":"Great logo","body":"Hi! Please have a look at this very nice logo of this cool company.","reference":"the-customers-reference","mediaUrls":["https://www.messagebird.com/assets/images/og/messagebird.gif"],"scheduledDatetime":null,"createdDatetime":"2017-09-01T10:00:00+00:00","recipients":{"totalCount":1,"totalSentCount":1,"totalDeliveredCount":0,"totalDeliveryFailedCount":0,"items":[{"recipient":18005551515,"status":"sent","statusDatetime":"2017-09-01T10:00:00+00:00"}]}}` invalidSecret = "bad_secret" @@ -92,7 +92,7 @@ var defaultReceiveTestCases = []IncomingTestCase{ ExpectedRespStatus: 200, ExpectedBodyContains: "Message Accepted", ExpectedMsgText: Sp("Test 3"), - ExpectedURN: "tel:188885551515", + ExpectedURN: "tel:+18885551515", ExpectedDate: time.Date(2023, time.July, 27, 00, 49, 29, 0, time.UTC), PrepRequest: addValidSignature, }, @@ -104,7 +104,7 @@ var defaultReceiveTestCases = []IncomingTestCase{ ExpectedRespStatus: 200, ExpectedBodyContains: "Message Accepted", ExpectedMsgText: Sp("Test 3"), - ExpectedURN: "tel:188885551515", + ExpectedURN: "tel:+18885551515", ExpectedDate: time.Date(2023, time.July, 27, 00, 49, 29, 0, time.UTC), PrepRequest: addValidSignature, }, @@ -116,7 +116,7 @@ var defaultReceiveTestCases = []IncomingTestCase{ ExpectedRespStatus: 200, ExpectedBodyContains: "Message Accepted", ExpectedAttachments: []string{"https://foo.bar/image.jpg"}, - ExpectedURN: "tel:188885551515", + ExpectedURN: "tel:+18885551515", ExpectedDate: time.Date(2023, time.July, 27, 00, 49, 29, 0, time.UTC), PrepRequest: addValidSignature, }, @@ -167,7 +167,7 @@ var defaultReceiveTestCases = []IncomingTestCase{ ExpectedRespStatus: 200, ExpectedStatuses: []ExpectedStatus{{MsgID: 26, Status: courier.MsgStatusFailed}}, ExpectedEvents: []ExpectedEvent{ - {Type: courier.EventTypeStopContact, URN: "tel:188885551515"}, + {Type: courier.EventTypeStopContact, URN: "tel:+18885551515"}, }, ExpectedErrors: []*courier.ChannelError{courier.ErrorExternal("103", "Contact has sent 'stop'")}, }, diff --git a/handlers/meta/handlers.go b/handlers/meta/handlers.go index 4005b6010..d7a357854 100644 --- a/handlers/meta/handlers.go +++ b/handlers/meta/handlers.go @@ -281,9 +281,9 @@ func (h *handler) processWhatsAppPayload(ctx context.Context, channel courier.Ch } date := parseTimestamp(ts) - urn, err := urns.NewWhatsAppURN(msg.From) + urn, err := urns.New(urns.WhatsApp, msg.From) if err != nil { - return nil, nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid whatsapp id")) } for _, msgError := range msg.Errors { @@ -420,14 +420,14 @@ func (h *handler) processFacebookInstagramPayload(ctx context.Context, channel c // create our URN if payload.Object == "instagram" { - urn, err = urns.NewInstagramURN(sender) + urn, err = urns.New(urns.Instagram, sender) if err != nil { - return nil, nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid instagram id")) } } else { - urn, err = urns.NewFacebookURN(sender) + urn, err = urns.New(urns.Facebook, sender) if err != nil { - return nil, nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid facebook id")) } } @@ -456,7 +456,7 @@ func (h *handler) processFacebookInstagramPayload(ctx context.Context, channel c // Right now that we even support this isn't documented and I don't think anybody uses it, so leaving that out. // (things will still work, we just will have dupe contacts, one with user_ref for the first contact, then with the real id when they reply) if msg.OptIn.UserRef != "" { - urn, err = urns.NewFacebookURN(urns.FacebookRefPrefix + msg.OptIn.UserRef) + urn, err = urns.New(urns.Facebook, urns.FacebookRefPrefix+msg.OptIn.UserRef) if err != nil { return nil, nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } @@ -642,8 +642,8 @@ func (h *handler) sendFacebookInstagramMsg(ctx context.Context, msg courier.MsgO payload := &messenger.SendRequest{} // build our recipient - if msg.URN().IsFacebookRef() { - payload.Recipient.UserRef = msg.URN().FacebookRef() + if IsFacebookRef(msg.URN()) { + payload.Recipient.UserRef = FacebookRef(msg.URN()) } else if msg.URNAuth() != "" { payload.Recipient.NotificationMessagesToken = msg.URNAuth() } else { @@ -740,15 +740,15 @@ func (h *handler) sendFacebookInstagramMsg(ctx context.Context, msg courier.MsgO } res.AddExternalID(respPayload.ExternalID) - if msg.URN().IsFacebookRef() { + if IsFacebookRef(msg.URN()) { recipientID := respPayload.RecipientID if recipientID == "" { return courier.ErrResponseUnexpected } - referralID := msg.URN().FacebookRef() + referralID := FacebookRef(msg.URN()) - realIDURN, err := urns.NewFacebookURN(recipientID) + realIDURN, err := urns.New(urns.Facebook, recipientID) if err != nil { clog.RawError(errors.Errorf("unable to make facebook urn from %s", recipientID)) } @@ -761,7 +761,7 @@ func (h *handler) sendFacebookInstagramMsg(ctx context.Context, msg courier.MsgO if err != nil { clog.RawError(errors.Errorf("unable to add real facebook URN %s to contact with uuid %s", realURN.String(), contact.UUID())) } - referralIDExtURN, err := urns.NewURNFromParts(urns.ExternalScheme, referralID, "", "") + referralIDExtURN, err := urns.New(urns.External, referralID) if err != nil { clog.RawError(errors.Errorf("unable to make ext urn from %s", referralID)) } @@ -1096,7 +1096,7 @@ func (h *handler) DescribeURN(ctx context.Context, channel courier.Channel, urn } // can't do anything with facebook refs, ignore them - if urn.IsFacebookRef() { + if IsFacebookRef(urn) { return map[string]string{}, nil } diff --git a/handlers/meta/urns.go b/handlers/meta/urns.go new file mode 100644 index 000000000..c57ece927 --- /dev/null +++ b/handlers/meta/urns.go @@ -0,0 +1,19 @@ +package meta + +import ( + "strings" + + "github.com/nyaruka/gocommon/urns" +) + +func IsFacebookRef(u urns.URN) bool { + return u.Scheme() == urns.Facebook.Prefix && strings.HasPrefix(u.Path(), urns.FacebookRefPrefix) +} + +// FacebookRef returns the facebook referral portion of our path, this return empty string in the case where we aren't a Facebook scheme +func FacebookRef(u urns.URN) string { + if IsFacebookRef(u) { + return strings.TrimPrefix(u.Path(), urns.FacebookRefPrefix) + } + return "" +} diff --git a/handlers/rocketchat/handler.go b/handlers/rocketchat/handler.go index a06b37011..c1718d4b6 100644 --- a/handlers/rocketchat/handler.go +++ b/handlers/rocketchat/handler.go @@ -69,7 +69,7 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("no text or attachment")) } - urn, err := urns.NewURNFromParts(urns.RocketChatScheme, payload.User.URN, "", payload.User.Username) + urn, err := urns.NewFromParts(urns.RocketChat, payload.User.URN, "", payload.User.Username) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } diff --git a/handlers/slack/handler.go b/handlers/slack/handler.go index 61fa0b3d8..2c668cdc5 100644 --- a/handlers/slack/handler.go +++ b/handlers/slack/handler.go @@ -75,7 +75,7 @@ func (h *handler) receiveEvent(ctx context.Context, channel courier.Channel, w h date := time.Unix(int64(payload.EventTime), 0) - urn, err := urns.NewURNFromParts(urns.SlackScheme, payload.Event.User, "", "") + urn, err := urns.New(urns.Slack, payload.Event.User) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } diff --git a/handlers/slack/handler_test.go b/handlers/slack/handler_test.go index 7a758dbdd..1b063e1ac 100644 --- a/handlers/slack/handler_test.go +++ b/handlers/slack/handler_test.go @@ -330,7 +330,7 @@ func TestDescribeURN(t *testing.T) { handler := newHandler() handler.Initialize(test.NewMockServer(courier.NewDefaultConfig(), test.NewMockBackend())) clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, testChannels[0], handler.RedactValues(testChannels[0])) - urn, _ := urns.NewURNFromParts(urns.SlackScheme, "U012345", "", "") + urn, _ := urns.New(urns.Slack, "U012345") data := map[string]string{"name": "dummy user"} diff --git a/handlers/telegram/handler.go b/handlers/telegram/handler.go index f39707dc3..3e9f7acf6 100644 --- a/handlers/telegram/handler.go +++ b/handlers/telegram/handler.go @@ -58,7 +58,7 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w date := time.Unix(payload.Message.Date, 0).UTC() // create our URN - urn, err := urns.NewTelegramURN(payload.Message.From.ContactID, strings.ToLower(payload.Message.From.Username)) + urn, err := urns.NewFromParts(urns.Telegram, strconv.FormatInt(payload.Message.From.ContactID, 10), "", strings.ToLower(payload.Message.From.Username)) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } diff --git a/handlers/telesom/handler.go b/handlers/telesom/handler.go index b2c5fac06..48e0f7368 100644 --- a/handlers/telesom/handler.go +++ b/handlers/telesom/handler.go @@ -12,6 +12,7 @@ import ( "github.com/nyaruka/courier" "github.com/nyaruka/courier/handlers" "github.com/nyaruka/gocommon/dates" + "github.com/nyaruka/gocommon/urns" ) var ( @@ -75,7 +76,7 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen for _, part := range handlers.SplitMsgByChannel(msg.Channel(), handlers.GetTextAndAttachments(msg), maxMsgLength) { from := strings.TrimPrefix(msg.Channel().Address(), "+") - to := fmt.Sprintf("0%s", strings.TrimPrefix(msg.URN().Localize(msg.Channel().Country()).Path(), "0")) + to := fmt.Sprintf("0%s", urns.ToLocalPhone(msg.URN(), msg.Channel().Country())) // build our request form := url.Values{ diff --git a/handlers/tembachat/handler.go b/handlers/tembachat/handler.go index 83e44a2b4..ecf7f2e36 100644 --- a/handlers/tembachat/handler.go +++ b/handlers/tembachat/handler.go @@ -63,9 +63,9 @@ func (h *handler) receive(ctx context.Context, c courier.Channel, w http.Respons return nil, handlers.WriteAndLogRequestError(ctx, h, c, w, r, errors.New("secret incorrect")) } - urn, err := urns.NewURNFromParts(urns.WebChatScheme, payload.ChatID, "", "") + urn, err := urns.New(urns.WebChat, payload.ChatID) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, c, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, c, w, r, errors.New("invalid chat id")) } events := make([]courier.Event, 0, 2) diff --git a/handlers/tembachat/handler_test.go b/handlers/tembachat/handler_test.go index cedc0e8f8..388473551 100644 --- a/handlers/tembachat/handler_test.go +++ b/handlers/tembachat/handler_test.go @@ -24,7 +24,7 @@ var incomingCases = []IncomingTestCase{ URL: "/c/twc/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/receive", Data: `{"chat_id": "xxxxx", "secret": "sesame", "events": [{"type": "msg_in", "msg": {"text": "Join"}}]}`, ExpectedRespStatus: 400, - ExpectedBodyContains: "invalid webchat id: xxxxx", + ExpectedBodyContains: "invalid chat id", }, { Label: "Chat started event", @@ -39,7 +39,7 @@ var incomingCases = []IncomingTestCase{ URL: "/c/twc/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/receive", Data: `{"chat_id": "xxxxx", "secret": "sesame", "events": [{"type": "chat_started"}]}`, ExpectedRespStatus: 400, - ExpectedBodyContains: "invalid webchat id: xxxxx", + ExpectedBodyContains: "invalid chat id", }, { Label: "Msg status update", diff --git a/handlers/twiml/handlers.go b/handlers/twiml/handlers.go index 575905486..b81824d52 100644 --- a/handlers/twiml/handlers.go +++ b/handlers/twiml/handlers.go @@ -24,6 +24,7 @@ import ( "github.com/nyaruka/courier/handlers" "github.com/nyaruka/courier/utils" "github.com/nyaruka/gocommon/httpx" + "github.com/nyaruka/gocommon/i18n" "github.com/nyaruka/gocommon/urns" ) @@ -125,7 +126,7 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } - urn, err := h.parseURN(channel, form.From, form.FromCountry) + urn, err := h.parseURN(channel, form.From, i18n.Country(form.FromCountry)) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } @@ -136,7 +137,7 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w } text := form.Body - if channel.IsScheme(urns.WhatsAppScheme) && form.ButtonText != "" { + if channel.IsScheme(urns.WhatsApp) && form.ButtonText != "" { text = form.ButtonText } @@ -259,9 +260,9 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen } // for whatsapp channels, we have to prepend whatsapp to the To and From - if channel.IsScheme(urns.WhatsAppScheme) { - form["To"][0] = fmt.Sprintf("%s:+%s", urns.WhatsAppScheme, form["To"][0]) - form["From"][0] = fmt.Sprintf("%s:%s", urns.WhatsAppScheme, form["From"][0]) + if channel.IsScheme(urns.WhatsApp) { + form["To"][0] = fmt.Sprintf("%s:+%s", urns.WhatsApp.Prefix, form["To"][0]) + form["From"][0] = fmt.Sprintf("%s:%s", urns.WhatsApp.Prefix, form["From"][0]) } // build our URL @@ -346,8 +347,8 @@ func (h *handler) RedactValues(ch courier.Channel) []string { } } -func (h *handler) parseURN(channel courier.Channel, text, country string) (urns.URN, error) { - if channel.IsScheme(urns.WhatsAppScheme) { +func (h *handler) parseURN(channel courier.Channel, text string, country i18n.Country) (urns.URN, error) { + if channel.IsScheme(urns.WhatsApp) { // Twilio Whatsapp from is in the form: whatsapp:+12211414154 or +12211414154 var fromTel string parts := strings.Split(text, ":") @@ -358,10 +359,10 @@ func (h *handler) parseURN(channel courier.Channel, text, country string) (urns. } // trim off left +, official whatsapp IDs dont have that - return urns.NewWhatsAppURN(strings.TrimLeft(fromTel, "+")) + return urns.New(urns.WhatsApp, strings.TrimLeft(fromTel, "+")) } - return urns.NewTelURNForCountry(text, country) + return urns.ParsePhone(text, country) } func (h *handler) baseURL(c courier.Channel) string { diff --git a/handlers/twiml/handlers_test.go b/handlers/twiml/handlers_test.go index 3481969c4..b4c7d523f 100644 --- a/handlers/twiml/handlers_test.go +++ b/handlers/twiml/handlers_test.go @@ -536,7 +536,7 @@ func TestIncoming(t *testing.T) { courier.ConfigAuthToken: "6789", }, ) - waChannel.SetScheme(urns.WhatsAppScheme) + waChannel.SetScheme(urns.WhatsApp.Prefix) RunIncomingTestCases(t, []courier.Channel{waChannel}, newTWIMLHandler("T", "TwilioWhatsApp", true), waTestCases) twaChannel := test.NewMockChannel("8eb23e93-5ecb-45ba-b726-3b064e0c56ab", "TWA", "+12065551212", "US", @@ -545,7 +545,7 @@ func TestIncoming(t *testing.T) { courier.ConfigAuthToken: "6789", }, ) - twaChannel.SetScheme(urns.WhatsAppScheme) + twaChannel.SetScheme(urns.WhatsApp.Prefix) RunIncomingTestCases(t, []courier.Channel{twaChannel}, newTWIMLHandler("TWA", "Twilio WhatsApp", true), twaTestCases) } @@ -1128,7 +1128,7 @@ func TestOutgoing(t *testing.T) { configSendURL: "http://example.com/sigware_api/", }, ) - waChannel.SetScheme(urns.WhatsAppScheme) + waChannel.SetScheme(urns.WhatsApp.Prefix) RunOutgoingTestCases(t, waChannel, newTWIMLHandler("T", "Twilio Whatsapp", true), waSendTestCases, []string{httpx.BasicAuth("accountSID", "authToken")}, nil) @@ -1138,7 +1138,7 @@ func TestOutgoing(t *testing.T) { courier.ConfigAuthToken: "authToken", }, ) - twaChannel.SetScheme(urns.WhatsAppScheme) + twaChannel.SetScheme(urns.WhatsApp.Prefix) RunOutgoingTestCases(t, twaChannel, newTWIMLHandler("TWA", "Twilio Whatsapp", true), twaSendTestCases, []string{httpx.BasicAuth("accountSID", "authToken")}, nil) } diff --git a/handlers/twitter/handler.go b/handlers/twitter/handler.go index 9a488845f..d8f2aef08 100644 --- a/handlers/twitter/handler.go +++ b/handlers/twitter/handler.go @@ -6,6 +6,7 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/base64" + "errors" "fmt" "io" "mime/multipart" @@ -166,9 +167,9 @@ func (h *handler) receiveEvents(ctx context.Context, c courier.Channel, w http.R return nil, handlers.WriteAndLogRequestError(ctx, h, c, w, r, fmt.Errorf("unable to find user for id: %s", senderID)) } - urn, err := urns.NewURNFromParts(urns.TwitterIDScheme, user.ID, "", strings.ToLower(user.ScreenName)) + urn, err := urns.NewFromParts(urns.TwitterID, user.ID, "", strings.ToLower(user.ScreenName)) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, c, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, c, w, r, errors.New("invalid twitter id")) } // create our date from the timestamp (they give us millis, arg is nanos) diff --git a/handlers/twitter/handler_test.go b/handlers/twitter/handler_test.go index d3e1da2c1..f42696f97 100644 --- a/handlers/twitter/handler_test.go +++ b/handlers/twitter/handler_test.go @@ -54,39 +54,6 @@ var helloMsg = `{ } }` -var invalidTwitterHandle = `{ - "direct_message_events": [ - { - "type": "message_create", - "id": "958501034212564996", - "created_timestamp": "1517359429301", - "message_create": { - "target": { - "recipient_id": "835740314006511618" - }, - "sender_id": "272953809", - "message_data": { - "text": "Hello World" - } - } - } - ], - "users": { - "272953809": { - "id": "272953809", - "created_timestamp": "1301236201000", - "name": "Nicolas Pottier", - "screen_name": "nicpottier!!$" - }, - "835740314006511618": { - "id": "835740314006511618", - "created_timestamp": "1488090992969", - "name": "Resistbot", - "screen_name": "resistbot" - } - } -}` - var invalidTwitterID = `{ "direct_message_events": [ { @@ -168,7 +135,6 @@ var testCases = []IncomingTestCase{ {Label: "Receive Attachment", URL: "/c/twt/8eb23e93-5ecb-45ba-b726-3b064e0c568c/receive", Data: attachment, ExpectedRespStatus: 200, ExpectedBodyContains: "Accepted", ExpectedMsgText: Sp("Hello"), ExpectedAttachments: []string{"https://image.foo.com/image.jpg"}, ExpectedURN: "twitterid:272953809#nicpottier", ExpectedExternalID: "958501034212564996", ExpectedDate: time.Date(2018, 1, 31, 0, 43, 49, 301000000, time.UTC)}, {Label: "Not JSON", URL: "/c/twt/8eb23e93-5ecb-45ba-b726-3b064e0c568c/receive", Data: notJSON, ExpectedRespStatus: 400, ExpectedBodyContains: "Error"}, - {Label: "Invalid Twitter handle", URL: "/c/twt/8eb23e93-5ecb-45ba-b726-3b064e0c568c/receive", Data: invalidTwitterHandle, ExpectedRespStatus: 400, ExpectedBodyContains: "invalid twitter handle"}, {Label: "Invalid Twitter ID", URL: "/c/twt/8eb23e93-5ecb-45ba-b726-3b064e0c568c/receive", Data: invalidTwitterID, ExpectedRespStatus: 400, ExpectedBodyContains: "invalid twitter id"}, {Label: "Webhook Verification", URL: "/c/twt/8eb23e93-5ecb-45ba-b726-3b064e0c568c/receive?crc_token=test+token", ExpectedRespStatus: 200, ExpectedBodyContains: "sha256=O5hJl2njQRIa4vsumZ+3oom9ECR5m3aQLRZkPoYelp0="}, diff --git a/handlers/utils.go b/handlers/utils.go index c00367da0..e1188c9a1 100644 --- a/handlers/utils.go +++ b/handlers/utils.go @@ -10,6 +10,7 @@ import ( "github.com/nyaruka/courier" "github.com/nyaruka/courier/utils" + "github.com/nyaruka/gocommon/i18n" "github.com/nyaruka/gocommon/urns" ) @@ -95,9 +96,9 @@ func DecodePossibleBase64(original string) string { // StrictTelForCountry wraps urns.NewURNTelForCountry but is stricter in // what it accepts. Incoming tels must be numeric or we will return an // error. (IE, alphanumeric shortcodes are not ok) -func StrictTelForCountry(number string, country string) (urns.URN, error) { +func StrictTelForCountry(number string, country i18n.Country) (urns.URN, error) { // first figure out if we are valid non-strictly - urn, err := urns.NewTelURNForCountry(number, country) + urn, err := urns.ParsePhone(number, country) if err != nil { return urns.NilURN, err } @@ -112,7 +113,7 @@ func StrictTelForCountry(number string, country string) (urns.URN, error) { // as our URN. This deals with the case where a carrier is handing us an E164 number that // the phonenumbers library doesn't know about yet if fmt.Sprintf("+%s", urn.Path()) == number && len(number) > 7 { - urn = urns.URN(urns.TelScheme + ":" + number) + urn = urns.URN(urns.Phone.Prefix + ":" + number) } return urn, nil diff --git a/handlers/viber/handler.go b/handlers/viber/handler.go index 8a2bf5651..ea917e8c7 100644 --- a/handlers/viber/handler.go +++ b/handlers/viber/handler.go @@ -141,9 +141,9 @@ func (h *handler) receiveEvent(ctx context.Context, channel courier.Channel, w h ContactName := payload.User.Name // build the URN - urn, err := urns.NewURNFromParts(urns.ViberScheme, viberID, "", "") + urn, err := urns.New(urns.Viber, viberID) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid viber id")) } // build the channel event channelEvent := h.Backend().NewChannelEvent(channel, courier.EventTypeWelcomeMessage, urn, clog).WithContactName(ContactName) @@ -162,9 +162,9 @@ func (h *handler) receiveEvent(ctx context.Context, channel courier.Channel, w h ContactName := payload.User.Name // build the URN - urn, err := urns.NewURNFromParts(urns.ViberScheme, viberID, "", "") + urn, err := urns.New(urns.Viber, viberID) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid viber id")) } // build the channel event @@ -183,9 +183,9 @@ func (h *handler) receiveEvent(ctx context.Context, channel courier.Channel, w h viberID := payload.UserID // build the URN - urn, err := urns.NewURNFromParts(urns.ViberScheme, viberID, "", "") + urn, err := urns.New(urns.Viber, viberID) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid viber id")) } // build the channel event channelEvent := h.Backend().NewChannelEvent(channel, courier.EventTypeStopContact, urn, clog) @@ -220,9 +220,9 @@ func (h *handler) receiveEvent(ctx context.Context, channel courier.Channel, w h contactName := payload.Sender.Name // create our URN - urn, err := urns.NewURNFromParts(urns.ViberScheme, sender, "", "") + urn, err := urns.New(urns.Viber, sender) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid viber id")) } text := payload.Message.Text diff --git a/handlers/vk/handler.go b/handlers/vk/handler.go index 03980ecf8..b1ed3d976 100644 --- a/handlers/vk/handler.go +++ b/handlers/vk/handler.go @@ -233,7 +233,7 @@ func (h *handler) verifyServer(channel courier.Channel, w http.ResponseWriter) ( // receiveMessage handles new message event func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w http.ResponseWriter, r *http.Request, payload *moNewMessagePayload, clog *courier.ChannelLog) ([]courier.Event, error) { userId := payload.Object.Message.UserId - urn, err := urns.NewURNFromParts(urns.VKScheme, strconv.FormatInt(userId, 10), "", "") + urn, err := urns.New(urns.VK, strconv.FormatInt(userId, 10)) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) diff --git a/handlers/vk/handler_test.go b/handlers/vk/handler_test.go index 81d424031..8b5d455bd 100644 --- a/handlers/vk/handler_test.go +++ b/handlers/vk/handler_test.go @@ -373,7 +373,7 @@ func TestDescribeURN(t *testing.T) { handler := newHandler() handler.Initialize(test.NewMockServer(courier.NewDefaultConfig(), test.NewMockBackend())) clog := courier.NewChannelLog(courier.ChannelLogTypeUnknown, testChannels[0], handler.RedactValues(testChannels[0])) - urn, _ := urns.NewURNFromParts(urns.VKScheme, "123456789", "", "") + urn, _ := urns.New(urns.VK, "123456789") data := map[string]string{"name": "John Doe"} describe, err := handler.(courier.URNDescriber).DescribeURN(context.Background(), testChannels[0], urn, clog) diff --git a/handlers/wechat/handler.go b/handlers/wechat/handler.go index 6f8adeadd..e0efff975 100644 --- a/handlers/wechat/handler.go +++ b/handlers/wechat/handler.go @@ -118,7 +118,7 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w } date := time.Unix(payload.CreateTime/1000, payload.CreateTime%1000*1000000).UTC() - urn, err := urns.NewURNFromParts(urns.WeChatScheme, payload.FromUsername, "", "") + urn, err := urns.New(urns.WeChat, payload.FromUsername) if err != nil { return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) } diff --git a/handlers/whatsapp_legacy/handler.go b/handlers/whatsapp_legacy/handler.go index 544c4b602..ccddd968e 100644 --- a/handlers/whatsapp_legacy/handler.go +++ b/handlers/whatsapp_legacy/handler.go @@ -202,9 +202,9 @@ func (h *handler) receiveEvents(ctx context.Context, channel courier.Channel, w date := time.Unix(ts, 0).UTC() // create our URN - urn, err := urns.NewWhatsAppURN(msg.From) + urn, err := urns.New(urns.WhatsApp, msg.From) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid whatsapp id")) } text := "" @@ -531,7 +531,7 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen if err == nil { // so update contact URN if wppID != "" if wppID != "" { - newURN, _ := urns.NewWhatsAppURN(wppID) + newURN, _ := urns.New(urns.WhatsApp, wppID) res.SetNewURN(newURN) } } diff --git a/handlers/zenvia/handlers.go b/handlers/zenvia/handlers.go index 3576b3e7b..7ca9ae754 100644 --- a/handlers/zenvia/handlers.go +++ b/handlers/zenvia/handlers.go @@ -3,6 +3,7 @@ package zenvia import ( "bytes" "context" + "errors" "fmt" "net/http" "strings" @@ -92,9 +93,9 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w } // create our URN - urn, err := urns.NewWhatsAppURN(payload.Message.From) + urn, err := urns.New(urns.WhatsApp, payload.Message.From) if err != nil { - return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err) + return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid whatsapp id")) } contactName := payload.Visitor.Name diff --git a/test/channel.go b/test/channel.go index 6f5157315..3ae284fb5 100644 --- a/test/channel.go +++ b/test/channel.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/nyaruka/courier" + "github.com/nyaruka/gocommon/i18n" "github.com/nyaruka/gocommon/urns" ) @@ -15,7 +16,7 @@ type MockChannel struct { channelType courier.ChannelType schemes []string address courier.ChannelAddress - country string + country i18n.Country role string config map[string]any orgConfig map[string]any @@ -37,8 +38,8 @@ func (c *MockChannel) SetScheme(scheme string) { c.schemes = []string{scheme} } func (c *MockChannel) Schemes() []string { return c.schemes } // IsScheme returns whether the passed in scheme is the scheme for this channel -func (c *MockChannel) IsScheme(scheme string) bool { - return len(c.schemes) == 1 && c.schemes[0] == scheme +func (c *MockChannel) IsScheme(scheme *urns.Scheme) bool { + return len(c.schemes) == 1 && c.schemes[0] == scheme.Prefix } // Address returns the address as a string of this channel @@ -48,7 +49,7 @@ func (c *MockChannel) Address() string { return c.address.String() } func (c *MockChannel) ChannelAddress() courier.ChannelAddress { return c.address } // Country returns the country this channel is for (if any) -func (c *MockChannel) Country() string { return c.country } +func (c *MockChannel) Country() i18n.Country { return c.country } // SetConfig sets the passed in config parameter func (c *MockChannel) SetConfig(key string, value any) { @@ -153,11 +154,11 @@ func (c *MockChannel) HasRole(role courier.ChannelRole) bool { } // NewMockChannel creates a new mock channel for the passed in type, address, country and config -func NewMockChannel(uuid string, channelType string, address string, country string, config map[string]any) *MockChannel { +func NewMockChannel(uuid string, channelType string, address string, country i18n.Country, config map[string]any) *MockChannel { return &MockChannel{ uuid: courier.ChannelUUID(uuid), channelType: courier.ChannelType(channelType), - schemes: []string{urns.TelScheme}, + schemes: []string{urns.Phone.Prefix}, address: courier.ChannelAddress(address), country: country, config: config,