├── .travis.yml ├── LICENSE ├── README.md ├── call.go ├── call_integration_test.go ├── call_list.go ├── call_test.go ├── client.go ├── errors.go ├── helper_test.go ├── incoming_phone_number.go ├── incoming_phone_number_list.go ├── incoming_phone_number_test.go ├── ip_channel.go ├── ip_client.go ├── ip_credential.go ├── ip_integration_test.go ├── ip_member.go ├── ip_message.go ├── ip_role.go ├── ip_service.go ├── ip_user.go ├── message.go ├── message_integration_test.go ├── message_list.go ├── message_test.go ├── mock_client.go └── optionals.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.1 5 | - 1.2 6 | - tip 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/carlosdp/twiliogo.png?branch=master)](https://travis-ci.org/carlosdp/twiliogo) 2 | # twilio-go 3 | The unofficial Go helper library for [Twilio](http://twilio.com). 4 | 5 | # Installation 6 | 7 | ``` bash 8 | go get github.com/carlosdp/twiliogo 9 | ``` 10 | 11 | # Documentation 12 | 13 | [GoDoc](http://godoc.org/github.com/carlosdp/twiliogo) 14 | 15 | # Usage 16 | 17 | ## Send a Text 18 | 19 | ``` go 20 | package main 21 | 22 | import ( 23 | "fmt" 24 | twilio "github.com/carlosdp/twiliogo" 25 | ) 26 | 27 | func main() { 28 | client := twilio.NewClient("", "") 29 | 30 | message, err := twilio.NewMessage(client, "3334445555", "2223334444", twilio.Body("Hello World!")) 31 | 32 | if err != nil { 33 | fmt.Println(err) 34 | } else { 35 | fmt.Println(message.Status) 36 | } 37 | } 38 | ``` 39 | 40 | ## Make a Call 41 | 42 | ``` go 43 | package main 44 | 45 | import ( 46 | "fmt" 47 | twilio "github.com/carlosdp/twiliogo" 48 | ) 49 | 50 | func main() { 51 | client := twilio.NewClient("", "") 52 | 53 | call, err := twilio.NewCall(client, "8883332222", "3334443333", nil) 54 | 55 | if err != nil { 56 | fmt.Println(err) 57 | } else { 58 | fmt.Println("Call Queued!") 59 | } 60 | } 61 | ``` 62 | 63 | ## Implemented Resources 64 | - Calls 65 | - Messages 66 | - IncomingPhoneNumbers (partial) 67 | 68 | ## Run Tests 69 | Tests can be run using `go test`, as with most golang projects. This project also contains integration tests (where they can be done non-destructively using the API or the working Test Credential endpoints). 70 | 71 | These integration tests can be run by providing the necessary environment variables to access the API, as in this Makefile: 72 | 73 | ```makefile 74 | test: 75 | @export API_KEY="";\ 76 | export API_TOKEN="";\ 77 | export TEST_KEY="";\ 78 | export TEST_TOKEN="";\ 79 | export TEST_FROM_NUMBER="";\ 80 | export FROM_NUMBER="";\ 81 | export TO_NUMBER="";\ 82 | go test -v 83 | ``` 84 | 85 | ## Contributing 86 | This is a side project meant to allow for quick adoption of the Twilio API for those programming web applications with it in Go. Feel free to submit pull requests so that we can cover all of the features the Twilio API has to offer! 87 | 88 | ## To Do 89 | Here are a few things that the project needs in order to reach v1.0: 90 | 91 | 1. Complete test coverage. Right now, tests cover the bare minimum of usage for each feature implemented. 92 | 2. Complete IncomingPhoneNumber functionality. 93 | 3. Implement the following resources: 94 | - AvailablePhoneNumbers 95 | - OutgoingCallerIds 96 | - Applications 97 | - ConnectApps 98 | - AuthorizedConnectApps 99 | - Conferences 100 | - Queues 101 | - Short Codes 102 | - Recordings 103 | - Transcriptions 104 | - Notifications 105 | - SIP Domains 106 | - IpAccessControlLists 107 | - CredentialLists 108 | - Usage Records 109 | - Usage Triggers 110 | 111 | ## License 112 | This project is licensed under the [MIT License](http://opensource.org/licenses/MIT) 113 | 114 | -------------------------------------------------------------------------------- /call.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | type Call struct { 9 | Sid string `json:"sid"` 10 | ParentCallSid string `json:"parent_call_sid"` 11 | DateCreated string `json:"date_created"` 12 | DateUpdated string `json:"date_updated"` 13 | AccountSid string `json:"account_sid"` 14 | To string `json:"to"` 15 | From string `json:"from"` 16 | PhoneNumberSid string `json:"phone_number_sid"` 17 | Status string `json:"status"` 18 | StartTime string `json:"start_time"` 19 | EndTime string `json:"end_time"` 20 | Duration string `json:"duration"` 21 | Price string `json:"price"` 22 | PriceUnit string `json:"price_unit"` 23 | Direction string `json:"direction"` 24 | AnsweredBy string `json:"answered_by"` 25 | ForwardedFrom string `json:"forwarded_from"` 26 | CallerName string `json:"caller_name"` 27 | Uri string `json:"uri"` 28 | } 29 | 30 | func NewCall(client Client, from, to string, callback Optional, optionals ...Optional) (*Call, error) { 31 | var call *Call 32 | 33 | params := url.Values{} 34 | params.Set("From", from) 35 | params.Set("To", to) 36 | 37 | callbackType, callbackParam := callback.GetParam() 38 | params.Set(callbackType, callbackParam) 39 | 40 | for _, optional := range optionals { 41 | param, value := optional.GetParam() 42 | params.Set(param, value) 43 | } 44 | 45 | res, err := client.post(params, "/Calls.json") 46 | 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | call = new(Call) 52 | err = json.Unmarshal(res, call) 53 | 54 | return call, err 55 | } 56 | 57 | func GetCall(client Client, sid string) (*Call, error) { 58 | var call *Call 59 | 60 | res, err := client.get(url.Values{}, "/Calls/"+sid+".json") 61 | 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | call = new(Call) 67 | err = json.Unmarshal(res, call) 68 | 69 | return call, err 70 | } 71 | 72 | func (call *Call) Update(client Client, optionals ...Optional) error { 73 | var tempCall *Call 74 | 75 | params := url.Values{} 76 | 77 | for _, optional := range optionals { 78 | param, value := optional.GetParam() 79 | params.Set(param, value) 80 | } 81 | 82 | res, err := client.post(params, "/Calls/"+call.Sid+".json") 83 | 84 | if err != nil { 85 | return err 86 | } 87 | 88 | tempCall = new(Call) 89 | err = json.Unmarshal(res, tempCall) 90 | 91 | if err == nil { 92 | call = tempCall 93 | } 94 | 95 | return err 96 | } 97 | -------------------------------------------------------------------------------- /call_integration_test.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestIntegrationCallList(t *testing.T) { 9 | CheckTestEnv(t) 10 | 11 | client := NewClient(API_KEY, API_TOKEN) 12 | 13 | callList, err := GetCallList(client) 14 | 15 | if assert.Nil(t, err, "Failed to retrieve call list") { 16 | calls := callList.GetCalls() 17 | 18 | assert.NotNil(t, calls, "Failed to retrieve calls") 19 | } 20 | } 21 | 22 | func TestIntegrationMakingCall(t *testing.T) { 23 | CheckTestEnv(t) 24 | 25 | client := NewClient(TEST_KEY, TEST_TOKEN) 26 | 27 | call, err := NewCall(client, TEST_FROM_NUMBER, TO_NUMBER, Callback("http://test.com")) 28 | 29 | if assert.Nil(t, err, "Failed to make call") { 30 | assert.Equal(t, call.Status, "queued", "Making Call failed, status: "+call.Status) 31 | } 32 | } 33 | 34 | func TestIntegrationCallListNextPage(t *testing.T) { 35 | CheckTestEnv(t) 36 | 37 | client := NewClient(API_KEY, API_TOKEN) 38 | 39 | callList, err := GetCallList(client) 40 | 41 | if assert.Nil(t, err, "Failed to retrieve call list") { 42 | nextPageCallList, err := callList.NextPage() 43 | 44 | if assert.Nil(t, err, "Failed to retrieve next page") { 45 | assert.Equal(t, nextPageCallList.Page, 1, "Page incorrect on next page") 46 | } 47 | } 48 | } 49 | 50 | func TestIntegrationGetCall(t *testing.T) { 51 | CheckTestEnv(t) 52 | 53 | client := NewClient(API_KEY, API_TOKEN) 54 | 55 | callList, err := GetCallList(client) 56 | 57 | if assert.Nil(t, err, "Failed to retrieve call list") { 58 | callSid := callList.Calls[0].Sid 59 | call, err := GetCall(client, callSid) 60 | 61 | if assert.Nil(t, err, "Failed to retrieve call") { 62 | assert.Equal(t, call.Sid, callSid, "Call was invalid") 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /call_list.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | type CallList struct { 9 | Client Client 10 | Start int `json:"start"` 11 | Total int `json:"total"` 12 | NumPages int `json:"num_pages"` 13 | Page int `json:"page"` 14 | PageSize int `json:"page_size"` 15 | End int `json:"end"` 16 | Uri string `json:"uri"` 17 | FirstPageUri string `json:"first_page_uri"` 18 | LastPageUri string `json:"last_page_uri"` 19 | NextPageUri string `json:"next_page_uri"` 20 | PreviousPageUri string `json"previous_page_uri"` 21 | Calls []Call `json:"calls"` 22 | } 23 | 24 | func GetCallList(client Client, optionals ...Optional) (*CallList, error) { 25 | var callList *CallList 26 | 27 | params := url.Values{} 28 | 29 | for _, optional := range optionals { 30 | param, value := optional.GetParam() 31 | params.Set(param, value) 32 | } 33 | 34 | body, err := client.get(nil, "/Calls.json") 35 | 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | callList = new(CallList) 41 | callList.Client = client 42 | err = json.Unmarshal(body, callList) 43 | 44 | return callList, err 45 | } 46 | 47 | func (callList *CallList) GetCalls() []Call { 48 | return callList.Calls 49 | } 50 | 51 | func (currentCallList *CallList) HasNextPage() bool { 52 | return currentCallList.NextPageUri != "" 53 | } 54 | 55 | func (currentCallList *CallList) NextPage() (*CallList, error) { 56 | if !currentCallList.HasNextPage() { 57 | return nil, Error{"No next page"} 58 | } 59 | 60 | return currentCallList.getPage(currentCallList.NextPageUri) 61 | } 62 | 63 | func (currentCallList *CallList) HasPreviousPage() bool { 64 | return currentCallList.PreviousPageUri != "" 65 | } 66 | 67 | func (currentCallList *CallList) PreviousPage() (*CallList, error) { 68 | if !currentCallList.HasPreviousPage() { 69 | return nil, Error{"No previous page"} 70 | } 71 | 72 | return currentCallList.getPage(currentCallList.NextPageUri) 73 | } 74 | 75 | func (currentCallList *CallList) FirstPage() (*CallList, error) { 76 | return currentCallList.getPage(currentCallList.FirstPageUri) 77 | } 78 | 79 | func (currentCallList *CallList) LastPage() (*CallList, error) { 80 | return currentCallList.getPage(currentCallList.LastPageUri) 81 | } 82 | 83 | func (currentCallList *CallList) getPage(uri string) (*CallList, error) { 84 | var callList *CallList 85 | 86 | client := currentCallList.Client 87 | 88 | body, err := client.get(nil, uri) 89 | 90 | if err != nil { 91 | return callList, err 92 | } 93 | 94 | callList = new(CallList) 95 | callList.Client = client 96 | err = json.Unmarshal(body, callList) 97 | 98 | return callList, err 99 | } 100 | -------------------------------------------------------------------------------- /call_test.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | var testCall = Call{ 12 | Sid: "testsid", 13 | ParentCallSid: "", 14 | DateCreated: "2013-05-11", 15 | DateUpdated: "2013-05-11", 16 | AccountSid: "AC3TestAccount", 17 | To: "+15555555555", 18 | From: "+16666666666", 19 | PhoneNumberSid: "", 20 | Status: "queued", 21 | StartTime: "5:00", 22 | EndTime: "6:00", 23 | Duration: "5", 24 | Price: "4", 25 | PriceUnit: "dollars", 26 | Direction: "outbound-api", 27 | AnsweredBy: "", 28 | ForwardedFrom: "", 29 | CallerName: "", 30 | Uri: "/2010-04-01/Accounts/AC3TestAccount/Calls/testsid.json", 31 | } 32 | 33 | func TestNewCall(t *testing.T) { 34 | client := new(MockClient) 35 | 36 | callJson, _ := json.Marshal(testCall) 37 | 38 | params := url.Values{} 39 | params.Set("From", "6666666666") 40 | params.Set("To", "5555555555") 41 | params.Set("Url", "http://callback.com") 42 | 43 | client.On("post", params, "/Calls.json").Return(callJson, nil) 44 | 45 | call, err := NewCall(client, "6666666666", "5555555555", Callback("http://callback.com")) 46 | 47 | client.Mock.AssertExpectations(t) 48 | 49 | if assert.Nil(t, err, "Error unmarshaling call") { 50 | assert.Equal(t, call.Sid, "testsid", "Call malformed") 51 | } 52 | } 53 | 54 | func TestGetCall(t *testing.T) { 55 | client := new(MockClient) 56 | 57 | callJson, _ := json.Marshal(testCall) 58 | 59 | client.On("get", url.Values{}, "/Calls/testsid.json").Return(callJson, nil) 60 | 61 | call, err := GetCall(client, "testsid") 62 | 63 | client.Mock.AssertExpectations(t) 64 | 65 | if assert.Nil(t, err, "Error unmarshaling call") { 66 | assert.Equal(t, call.Sid, "testsid", "Call malformed") 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/url" 9 | "strings" 10 | ) 11 | 12 | const ROOT = "https://api.twilio.com" 13 | const VERSION = "2010-04-01" 14 | 15 | type Client interface { 16 | AccountSid() string 17 | AuthToken() string 18 | RootUrl() string 19 | get(url.Values, string) ([]byte, error) 20 | post(url.Values, string) ([]byte, error) 21 | delete(string) error 22 | } 23 | 24 | type TwilioClient struct { 25 | accountSid string 26 | authToken string 27 | rootUrl string 28 | } 29 | 30 | var _ Client = &TwilioClient{} 31 | 32 | func NewClient(accountSid, authToken string) *TwilioClient { 33 | rootUrl := ROOT + "/" + VERSION + "/Accounts/" + accountSid 34 | return &TwilioClient{accountSid, authToken, rootUrl} 35 | } 36 | 37 | func (client *TwilioClient) post(values url.Values, uri string) ([]byte, error) { 38 | req, err := http.NewRequest("POST", client.buildUri(uri), strings.NewReader(values.Encode())) 39 | 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | req.SetBasicAuth(client.AccountSid(), client.AuthToken()) 45 | req.Header.Add("Content-Type", "application/x-www-form-urlencoded") 46 | httpClient := &http.Client{} 47 | 48 | res, err := httpClient.Do(req) 49 | 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | defer res.Body.Close() 55 | 56 | body, err := ioutil.ReadAll(res.Body) 57 | 58 | if err != nil { 59 | return body, err 60 | } 61 | 62 | if res.StatusCode != 200 && res.StatusCode != 201 { 63 | if res.StatusCode == 500 { 64 | return body, Error{"Server Error"} 65 | } else { 66 | twilioError := new(TwilioError) 67 | json.Unmarshal(body, twilioError) 68 | return body, twilioError 69 | } 70 | } 71 | 72 | return body, err 73 | } 74 | 75 | func (client *TwilioClient) get(queryParams url.Values, uri string) ([]byte, error) { 76 | var params *strings.Reader 77 | 78 | if queryParams == nil { 79 | queryParams = url.Values{} 80 | } 81 | 82 | params = strings.NewReader(queryParams.Encode()) 83 | req, err := http.NewRequest("GET", client.buildUri(uri), params) 84 | 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | req.SetBasicAuth(client.AccountSid(), client.AuthToken()) 90 | httpClient := &http.Client{} 91 | 92 | res, err := httpClient.Do(req) 93 | 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | defer res.Body.Close() 99 | 100 | body, err := ioutil.ReadAll(res.Body) 101 | 102 | if err != nil { 103 | return body, err 104 | } 105 | 106 | if res.StatusCode != 200 && res.StatusCode != 201 { 107 | if res.StatusCode == 500 { 108 | return body, Error{"Server Error"} 109 | } else { 110 | twilioError := new(TwilioError) 111 | json.Unmarshal(body, twilioError) 112 | return body, twilioError 113 | } 114 | } 115 | 116 | return body, err 117 | } 118 | 119 | func (client *TwilioClient) delete(uri string) error { 120 | req, err := http.NewRequest("DELETE", client.buildUri(uri), nil) 121 | 122 | if err != nil { 123 | return err 124 | } 125 | 126 | req.SetBasicAuth(client.AccountSid(), client.AuthToken()) 127 | httpClient := &http.Client{} 128 | 129 | res, err := httpClient.Do(req) 130 | 131 | if err != nil { 132 | return err 133 | } 134 | 135 | defer res.Body.Close() 136 | 137 | if res.StatusCode != 204 { 138 | return fmt.Errorf("Non-204 returned from server for DELETE: %d", res.StatusCode) 139 | } 140 | 141 | return nil 142 | } 143 | 144 | func (client *TwilioClient) AccountSid() string { 145 | return client.accountSid 146 | } 147 | 148 | func (client *TwilioClient) AuthToken() string { 149 | return client.authToken 150 | } 151 | 152 | func (client *TwilioClient) RootUrl() string { 153 | return client.rootUrl 154 | } 155 | 156 | func (client *TwilioClient) buildUri(parts ...string) string { 157 | if len(parts) == 0 { 158 | return "" 159 | } 160 | 161 | newParts := make([]string, 0, len(parts)) 162 | // Check for "http" because sometimes we get raw URLs from following the metadata. 163 | if !strings.HasPrefix(parts[0], "http") { 164 | newParts = append(newParts, client.RootUrl()) 165 | } 166 | for _, p := range parts { 167 | p = strings.Trim(p, "/") 168 | if p == "" { 169 | continue 170 | } 171 | newParts = append(newParts, p) 172 | } 173 | return strings.Join(newParts, "/") 174 | } 175 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import "fmt" 4 | 5 | type Error struct { 6 | Description string 7 | } 8 | 9 | func (e Error) Error() string { 10 | return e.Description 11 | } 12 | 13 | type TwilioError struct { 14 | Status int `json:"status"` 15 | Message string `json:"message"` 16 | Code int `json:"code"` 17 | MoreInfo string `json:"more_info"` 18 | } 19 | 20 | func (e TwilioError) Error() string { 21 | var message string 22 | 23 | message = "Twilio Error, " 24 | 25 | if e.Status != 0 { 26 | message += fmt.Sprintf("Status: %d", e.Status) 27 | } 28 | 29 | if e.Code != 0 { 30 | message += fmt.Sprintf(", Code: %d", e.Code) 31 | } 32 | 33 | if e.Message != "" { 34 | message += ", Message: " + e.Message 35 | } 36 | 37 | return message 38 | } 39 | -------------------------------------------------------------------------------- /helper_test.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | var API_KEY string = os.Getenv("API_KEY") 9 | var API_TOKEN string = os.Getenv("API_TOKEN") 10 | var FROM_NUMBER string = os.Getenv("FROM_NUMBER") 11 | 12 | var TEST_KEY string = os.Getenv("TEST_KEY") 13 | var TEST_TOKEN string = os.Getenv("TEST_TOKEN") 14 | var TEST_FROM_NUMBER string = os.Getenv("TEST_FROM_NUMBER") 15 | 16 | var TO_NUMBER string = os.Getenv("TO_NUMBER") 17 | 18 | func CheckTestEnv(t *testing.T) { 19 | if API_KEY == "" || API_TOKEN == "" || 20 | TEST_KEY == "" || TEST_TOKEN == "" || 21 | TEST_FROM_NUMBER == "" || FROM_NUMBER == "" || 22 | TO_NUMBER == "" { 23 | t.SkipNow() 24 | } 25 | } 26 | 27 | func TestBuildUri(t *testing.T) { 28 | c := NewClient("abc", "") 29 | uri := c.buildUri("qzx") 30 | if uri != ROOT+"/"+VERSION+"/Accounts/abc/qzx" { 31 | t.Errorf("buildUri failed: got %s", uri) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /incoming_phone_number.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | type Capabilites struct { 9 | Voice bool `json:"voice"` 10 | SMS bool `json:"SMS"` 11 | MMS bool `json:"MMS"` 12 | } 13 | 14 | type IncomingPhoneNumber struct { 15 | Sid string `json:"sid"` 16 | AccountSid string `json:"account_sid"` 17 | FriendlyName string `json:"friendly_name"` 18 | PhoneNumber string `json:"phone_number"` 19 | VoiceUrl string `json:"voice_url"` 20 | VoiceMethod string `json:"voice_method"` 21 | VoiceFallbackUrl string `json:"voice_fallback_url"` 22 | VoiceFallbackMethod string `json:"voice_fallback_method"` 23 | StatusCallback string `json:"status_callback"` 24 | StatusCallbackMethod string `json:"status_callback_method"` 25 | VoiceCallerIdLookup bool `json:"voice_caller_id_lookup"` 26 | VoiceApplicationId string `json:"voice_application_id"` 27 | DateCreated string `json:"date_created"` 28 | DateUpdated string `json:"date_updated"` 29 | SmsUrl string `json:"sms_url"` 30 | SmsMethod string `json:"sms_method"` 31 | SmsFallbackUrl string `json:"sms_fallback_url"` 32 | SmsFallbackMethod string `json:"sms_fallback_method"` 33 | SmsApplicationId string `json:"sms_application_id"` 34 | Capabilities Capabilites `json:"capabilities"` 35 | ApiVersion string `json:"api_version"` 36 | Uri string `json:"uri"` 37 | } 38 | 39 | func GetIncomingPhoneNumber(client Client, sid string) (*IncomingPhoneNumber, error) { 40 | var incomingPhoneNumber *IncomingPhoneNumber 41 | 42 | res, err := client.get(url.Values{}, "/IncomingPhoneNumbers/"+sid+".json") 43 | 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | incomingPhoneNumber = new(IncomingPhoneNumber) 49 | err = json.Unmarshal(res, incomingPhoneNumber) 50 | 51 | return incomingPhoneNumber, err 52 | } 53 | 54 | func BuyPhoneNumber(client Client, number Optional) (*IncomingPhoneNumber, error) { 55 | var incomingPhoneNumber *IncomingPhoneNumber 56 | 57 | if number == nil { 58 | return nil, Error{"Must input PhoneNumber or AreaCode"} 59 | } 60 | 61 | params := url.Values{} 62 | param, value := number.GetParam() 63 | params.Set(param, value) 64 | 65 | res, err := client.post(params, "/IncomingPhoneNumbers.json") 66 | 67 | if err != nil { 68 | return incomingPhoneNumber, err 69 | } 70 | 71 | incomingPhoneNumber = new(IncomingPhoneNumber) 72 | err = json.Unmarshal(res, incomingPhoneNumber) 73 | 74 | return incomingPhoneNumber, err 75 | } 76 | -------------------------------------------------------------------------------- /incoming_phone_number_list.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | type IncomingPhoneNumberList struct { 9 | Client Client 10 | Start int `json:"start"` 11 | Total int `json:"total"` 12 | NumPages int `json:"num_pages"` 13 | Page int `json:"page"` 14 | PageSize int `json:"page_size"` 15 | End int `json:"end"` 16 | Uri string `json:"uri"` 17 | FirstPageUri string `json:"first_page_uri"` 18 | LastPageUri string `json:"last_page_uri"` 19 | NextPageUri string `json:"next_page_uri"` 20 | PreviousPageUri string `json"previous_page_uri"` 21 | IncomingPhoneNumbers []IncomingPhoneNumber `json:"incoming_phone_numbers"` 22 | } 23 | 24 | func GetIncomingPhoneNumberList(client Client, optionals ...Optional) (*IncomingPhoneNumberList, error) { 25 | var incomingPhoneNumberList *IncomingPhoneNumberList 26 | 27 | params := url.Values{} 28 | 29 | for _, optional := range optionals { 30 | param, value := optional.GetParam() 31 | params.Set(param, value) 32 | } 33 | 34 | body, err := client.get(params, "/IncomingPhoneNumbers.json") 35 | 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | incomingPhoneNumberList = new(IncomingPhoneNumberList) 41 | incomingPhoneNumberList.Client = client 42 | err = json.Unmarshal(body, incomingPhoneNumberList) 43 | 44 | return incomingPhoneNumberList, err 45 | } 46 | -------------------------------------------------------------------------------- /incoming_phone_number_test.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | var testNumber = IncomingPhoneNumber{ 12 | Sid: "testsid", 13 | AccountSid: "AC3TestAccount", 14 | FriendlyName: "testname", 15 | PhoneNumber: "+1 (444) 444-4444", 16 | VoiceUrl: "http://test.com", 17 | VoiceMethod: "POST", 18 | VoiceFallbackUrl: "http://fail.com", 19 | VoiceFallbackMethod: "GET", 20 | VoiceCallerIdLookup: true, 21 | StatusCallback: "http://status.com", 22 | StatusCallbackMethod: "GET", 23 | SmsUrl: "http://sms.com", 24 | SmsMethod: "GET", 25 | DateCreated: "2013-05-11", 26 | DateUpdated: "2013-05-11", 27 | Capabilities: Capabilites{true, true, true}, 28 | ApiVersion: "2008-04-01", 29 | Uri: "/2010-04-01/Accounts/AC3TestAccount/Messages/testsid.json", 30 | } 31 | 32 | func TestBuyPhoneNumber(t *testing.T) { 33 | client := new(MockClient) 34 | 35 | numberJson, _ := json.Marshal(testNumber) 36 | 37 | params := url.Values{} 38 | params.Set("PhoneNumber", "4444444444") 39 | 40 | client.On("post", params, "/IncomingPhoneNumbers.json").Return(numberJson, nil) 41 | 42 | number, err := BuyPhoneNumber(client, PhoneNumber("4444444444")) 43 | 44 | client.Mock.AssertExpectations(t) 45 | 46 | if assert.Nil(t, err, "Error unmarshaling number") { 47 | assert.Equal(t, number.Sid, "testsid", "Number malformed") 48 | } 49 | } 50 | 51 | func TestGetIncomingPhoneNumber(t *testing.T) { 52 | client := new(MockClient) 53 | 54 | numberJson, _ := json.Marshal(testNumber) 55 | 56 | client.On("get", url.Values{}, "/IncomingPhoneNumbers/testsid.json").Return(numberJson, nil) 57 | 58 | number, err := GetIncomingPhoneNumber(client, "testsid") 59 | 60 | client.Mock.AssertExpectations(t) 61 | 62 | if assert.Nil(t, err, "Error unmarshaling number") { 63 | assert.Equal(t, number.Sid, "testsid", "Number malformed") 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ip_channel.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | // IPChannel is a IP Messaging Channel resource. 9 | type IPChannel struct { 10 | Sid string `json:"sid"` 11 | AccountSid string `json:"account_sid"` 12 | ServiceSid string `json:"service_sid"` 13 | FriendlyName string `json:"friendly_name"` 14 | UniqueName string `json:"unique_name"` 15 | Attributes string `json:"attributes"` 16 | Type string `json:"type"` 17 | DateCreated string `json:"date_created"` 18 | DateUpdated string `json:"date_updated"` 19 | CreatedBy string `json:"created_by"` 20 | URL string `json:"url"` 21 | Links map[string]string `json:"links"` 22 | } 23 | 24 | // IPChannelList gives the results for querying the set of channels. Returns the first page 25 | // by default. 26 | type IPChannelList struct { 27 | Client Client 28 | Channels []IPChannel `json:"channels"` 29 | Meta Meta `json:"meta"` 30 | } 31 | 32 | // NewIPChannel creates a new IP Messaging Channel. 33 | func NewIPChannel(client *TwilioIPMessagingClient, serviceSid string, friendlyName string, uniqueName string, public bool, attributes string) (*IPChannel, error) { 34 | var channel *IPChannel 35 | 36 | params := url.Values{} 37 | params.Set("FriendlyName", friendlyName) 38 | params.Set("UniqueName", uniqueName) 39 | kind := "private" 40 | if public { 41 | kind = "public" 42 | } 43 | params.Set("Type", kind) 44 | params.Set("Attributes", attributes) 45 | 46 | res, err := client.post(params, "/Services/"+serviceSid+"/Channels.json") 47 | 48 | if err != nil { 49 | return channel, err 50 | } 51 | 52 | channel = new(IPChannel) 53 | err = json.Unmarshal(res, channel) 54 | 55 | return channel, err 56 | } 57 | 58 | // UpdateIPChannel updates ane existing IP Messaging Channel. 59 | func UpdateIPChannel(client *TwilioIPMessagingClient, serviceSid string, sid string, friendlyName string, uniqueName string, public bool, attributes string) (*IPChannel, error) { 60 | var channel *IPChannel 61 | 62 | params := url.Values{} 63 | params.Set("FriendlyName", friendlyName) 64 | params.Set("UniqueName", uniqueName) 65 | kind := "private" 66 | if public { 67 | kind = "public" 68 | } 69 | params.Set("Type", kind) 70 | params.Set("Attributes", attributes) 71 | 72 | res, err := client.post(params, "/Services/"+serviceSid+"/Channels/"+sid+".json") 73 | 74 | if err != nil { 75 | return channel, err 76 | } 77 | 78 | channel = new(IPChannel) 79 | err = json.Unmarshal(res, channel) 80 | 81 | return channel, err 82 | } 83 | 84 | // GetIPChannel returns the specified IP Channel. 85 | func GetIPChannel(client *TwilioIPMessagingClient, serviceSid string, sid string) (*IPChannel, error) { 86 | var channel *IPChannel 87 | 88 | res, err := client.get(url.Values{}, "/Services/"+serviceSid+"/Channels/"+sid+".json") 89 | 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | channel = new(IPChannel) 95 | err = json.Unmarshal(res, channel) 96 | 97 | return channel, err 98 | } 99 | 100 | // DeleteIPChannel deletes the given IP Channel. 101 | func DeleteIPChannel(client *TwilioIPMessagingClient, serviceSid, sid string) error { 102 | return client.delete("/Services/" + serviceSid + "/Channels/" + sid) 103 | } 104 | 105 | // ListIPChannels returns the first page of channels. 106 | func ListIPChannels(client *TwilioIPMessagingClient, serviceSid string) (*IPChannelList, error) { 107 | var channelList *IPChannelList 108 | 109 | body, err := client.get(nil, "/Services/"+serviceSid+"/Channels.json") 110 | 111 | if err != nil { 112 | return channelList, err 113 | } 114 | 115 | channelList = new(IPChannelList) 116 | channelList.Client = client 117 | err = json.Unmarshal(body, channelList) 118 | 119 | return channelList, err 120 | } 121 | 122 | // GetChannels recturns the current page of channels. 123 | func (c *IPChannelList) GetChannels() []IPChannel { 124 | return c.Channels 125 | } 126 | 127 | // GetAllChannels returns all of the channels from all of the pages (from here forward). 128 | func (c *IPChannelList) GetAllChannels() ([]IPChannel, error) { 129 | channels := c.Channels 130 | t := c 131 | 132 | for t.HasNextPage() { 133 | var err error 134 | t, err = t.NextPage() 135 | if err != nil { 136 | return nil, err 137 | } 138 | channels = append(channels, t.Channels...) 139 | } 140 | return channels, nil 141 | } 142 | 143 | // HasNextPage returns whether or not there is a next page of channels. 144 | func (c *IPChannelList) HasNextPage() bool { 145 | return c.Meta.NextPageUri != "" 146 | } 147 | 148 | // NextPage returns the next page of channels. 149 | func (c *IPChannelList) NextPage() (*IPChannelList, error) { 150 | if !c.HasNextPage() { 151 | return nil, Error{"No next page"} 152 | } 153 | 154 | return c.getPage(c.Meta.NextPageUri) 155 | } 156 | 157 | // HasPreviousPage indicates whether or not there is a previous page of results. 158 | func (c *IPChannelList) HasPreviousPage() bool { 159 | return c.Meta.PreviousPageUri != "" 160 | } 161 | 162 | // PreviousPage returns the previous page of channels. 163 | func (c *IPChannelList) PreviousPage() (*IPChannelList, error) { 164 | if !c.HasPreviousPage() { 165 | return nil, Error{"No previous page"} 166 | } 167 | 168 | return c.getPage(c.Meta.NextPageUri) 169 | } 170 | 171 | // FirstPage returns the first page of channels. 172 | func (c *IPChannelList) FirstPage() (*IPChannelList, error) { 173 | return c.getPage(c.Meta.FirstPageUri) 174 | } 175 | 176 | // LastPage returns the last page of channels. 177 | func (c *IPChannelList) LastPage() (*IPChannelList, error) { 178 | return c.getPage(c.Meta.LastPageUri) 179 | } 180 | 181 | func (c *IPChannelList) getPage(uri string) (*IPChannelList, error) { 182 | var channelList *IPChannelList 183 | 184 | client := c.Client 185 | 186 | body, err := client.get(nil, uri) 187 | 188 | if err != nil { 189 | return channelList, err 190 | } 191 | 192 | channelList = new(IPChannelList) 193 | channelList.Client = client 194 | err = json.Unmarshal(body, channelList) 195 | 196 | return channelList, err 197 | } 198 | -------------------------------------------------------------------------------- /ip_client.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | // Constants for the IP Messaging service. 4 | const ( 5 | IP_MESSAGING_ROOT = "https://ip-messaging.twilio.com" 6 | IP_MESSAGING_VERSION = "v1" 7 | IP_MESSAGING_ROOT_URL = IP_MESSAGING_ROOT + "/" + IP_MESSAGING_VERSION 8 | ) 9 | 10 | // TwilioIPMessagingClient is used for accessing the Twilio IP Messaging API. 11 | type TwilioIPMessagingClient struct { 12 | TwilioClient 13 | } 14 | 15 | var _ Client = &TwilioIPMessagingClient{} 16 | 17 | // NewIPMessagingClient creates a new Twilio IP Messaging client. 18 | func NewIPMessagingClient(accountSid, authToken string) *TwilioIPMessagingClient { 19 | rootUrl := IP_MESSAGING_ROOT + "/" + IP_MESSAGING_VERSION 20 | return &TwilioIPMessagingClient{TwilioClient{accountSid, authToken, rootUrl}} 21 | } 22 | -------------------------------------------------------------------------------- /ip_credential.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | // IPCredential is a IP Messaging Credential resource. 9 | type IPCredential struct { 10 | Sid string `json:"sid"` 11 | AccountSid string `json:"account_sid"` 12 | FriendlyName string `json:"friendly_name"` 13 | Type string `json:"type"` // apns or gcm 14 | Sandbox bool `json:"sandbox"` 15 | URL string `json:"url"` 16 | } 17 | 18 | // IPCredentialList gives the results for querying the set of credentials. Returns the first page 19 | // by default. 20 | type IPCredentialList struct { 21 | Client Client 22 | Credentials []IPCredential `json:"credentials"` 23 | Meta Meta `json:"meta"` 24 | } 25 | 26 | // NewIPCredential creates a new IP Messaging Credential. 27 | // Kind must be apns or gcm. 28 | func NewIPCredential(client *TwilioIPMessagingClient, friendlyName string, kind string, sandbox bool, apnsCert string, apnsPrivateKey string, 29 | gcmApiKey string) (*IPCredential, error) { 30 | var credential *IPCredential 31 | 32 | params := url.Values{} 33 | params.Set("FriendlyName", friendlyName) 34 | params.Set("Type", kind) 35 | if sandbox { 36 | params.Set("Sandbox", "true") 37 | } else { 38 | params.Set("Sandbox", "false") 39 | } 40 | if apnsCert != "" { 41 | params.Set("Certificate", apnsCert) 42 | } 43 | if apnsPrivateKey != "" { 44 | params.Set("PrivateKey", apnsPrivateKey) 45 | } 46 | if gcmApiKey != "" { 47 | params.Set("ApiKey", gcmApiKey) 48 | } 49 | 50 | res, err := client.post(params, "/Credentials.json") 51 | 52 | if err != nil { 53 | return credential, err 54 | } 55 | 56 | credential = new(IPCredential) 57 | err = json.Unmarshal(res, credential) 58 | 59 | return credential, err 60 | } 61 | 62 | // GetIPCredential returns information on the specified credential. 63 | func GetIPCredential(client *TwilioIPMessagingClient, sid string) (*IPCredential, error) { 64 | var credential *IPCredential 65 | 66 | res, err := client.get(url.Values{}, "/Credentials/"+sid+".json") 67 | 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | credential = new(IPCredential) 73 | err = json.Unmarshal(res, credential) 74 | 75 | return credential, err 76 | } 77 | 78 | // DeleteIPCredential deletes the given IP Credential. 79 | func DeleteIPCredential(client *TwilioIPMessagingClient, sid string) error { 80 | return client.delete("/Credentials/" + sid) 81 | } 82 | 83 | // UpdateIPCredential updates an existing IP Messaging Credential. 84 | func UpdateIPCredential(client *TwilioIPMessagingClient, sid string, friendlyName string, kind string, sandbox bool) (*IPCredential, error) { 85 | var credential *IPCredential 86 | 87 | params := url.Values{} 88 | params.Set("FriendlyName", friendlyName) 89 | params.Set("Type", kind) 90 | if sandbox { 91 | params.Set("Sandbox", "true") 92 | } else { 93 | params.Set("Sandbox", "false") 94 | } 95 | 96 | res, err := client.post(params, "/Credentials/"+sid+".json") 97 | 98 | if err != nil { 99 | return credential, err 100 | } 101 | 102 | credential = new(IPCredential) 103 | err = json.Unmarshal(res, credential) 104 | 105 | return credential, err 106 | } 107 | 108 | // ListIPCredentials returns the first page of credentials. 109 | func ListIPCredentials(client *TwilioIPMessagingClient) (*IPCredentialList, error) { 110 | var credentialList *IPCredentialList 111 | 112 | body, err := client.get(nil, "/Credentials.json") 113 | 114 | if err != nil { 115 | return credentialList, err 116 | } 117 | 118 | credentialList = new(IPCredentialList) 119 | credentialList.Client = client 120 | err = json.Unmarshal(body, credentialList) 121 | 122 | return credentialList, err 123 | } 124 | 125 | // GetCredentials returns the current page of credentials. 126 | func (s *IPCredentialList) GetCredentials() []IPCredential { 127 | return s.Credentials 128 | } 129 | 130 | // GetAllCredentials returns all of the credentials from all of the pages (from here forward). 131 | func (s *IPCredentialList) GetAllCredentials() ([]IPCredential, error) { 132 | credentials := s.Credentials 133 | t := s 134 | 135 | for t.HasNextPage() { 136 | var err error 137 | t, err = t.NextPage() 138 | if err != nil { 139 | return nil, err 140 | } 141 | credentials = append(credentials, t.Credentials...) 142 | } 143 | return credentials, nil 144 | } 145 | 146 | // HasNextPage returns whether or not there is a next page of credentials. 147 | func (s *IPCredentialList) HasNextPage() bool { 148 | return s.Meta.NextPageUri != "" 149 | } 150 | 151 | // NextPage returns the next page of credentials. 152 | func (s *IPCredentialList) NextPage() (*IPCredentialList, error) { 153 | if !s.HasNextPage() { 154 | return nil, Error{"No next page"} 155 | } 156 | 157 | return s.getPage(s.Meta.NextPageUri) 158 | } 159 | 160 | // HasPreviousPage indicates whether or not there is a previous page of results. 161 | func (s *IPCredentialList) HasPreviousPage() bool { 162 | return s.Meta.PreviousPageUri != "" 163 | } 164 | 165 | // PreviousPage returns the previous page of credentials. 166 | func (s *IPCredentialList) PreviousPage() (*IPCredentialList, error) { 167 | if !s.HasPreviousPage() { 168 | return nil, Error{"No previous page"} 169 | } 170 | 171 | return s.getPage(s.Meta.NextPageUri) 172 | } 173 | 174 | // FirstPage returns the first page of credentials. 175 | func (s *IPCredentialList) FirstPage() (*IPCredentialList, error) { 176 | return s.getPage(s.Meta.FirstPageUri) 177 | } 178 | 179 | // LastPage returns the last page of credentials. 180 | func (s *IPCredentialList) LastPage() (*IPCredentialList, error) { 181 | return s.getPage(s.Meta.LastPageUri) 182 | } 183 | 184 | func (s *IPCredentialList) getPage(uri string) (*IPCredentialList, error) { 185 | var credentialList *IPCredentialList 186 | 187 | client := s.Client 188 | 189 | body, err := client.get(nil, uri) 190 | 191 | if err != nil { 192 | return credentialList, err 193 | } 194 | 195 | credentialList = new(IPCredentialList) 196 | credentialList.Client = client 197 | err = json.Unmarshal(body, credentialList) 198 | 199 | return credentialList, err 200 | } 201 | -------------------------------------------------------------------------------- /ip_integration_test.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestIntegrationIPMessaging(t *testing.T) { 12 | CheckTestEnv(t) 13 | 14 | client := NewIPMessagingClient(API_KEY, API_TOKEN) 15 | 16 | service, err := NewIPService(client, "integration_test", "", "", 60*time.Second, nil) 17 | 18 | if !assert.Nil(t, err, fmt.Sprintf("Failed to create service: %v", err)) { 19 | return 20 | } 21 | ssid := service.Sid 22 | if !assert.NotEqual(t, "", ssid, "Service SID was empty") { 23 | return 24 | } 25 | 26 | defer DeleteIPService(client, ssid) 27 | serviceList, err := ListIPServices(client) 28 | if !assert.Nil(t, err, fmt.Sprintf("Failed to retrieve service list: %v", err)) { 29 | return 30 | } 31 | services, err := serviceList.GetAllServices() 32 | if !assert.Nil(t, err, fmt.Sprintf("Failed to get all services: %v", err)) { 33 | return 34 | } 35 | 36 | found := false 37 | for _, s := range services { 38 | if s.FriendlyName == "integration_test" && s.Sid == ssid { 39 | found = true 40 | break 41 | } 42 | } 43 | if !assert.True(t, found, "Could not find service") { 44 | return 45 | } 46 | 47 | channel, err := NewIPChannel(client, ssid, "integration-channel", "", false, "") 48 | if !assert.Nil(t, err, fmt.Sprintf("Failed to create channel: %v", err)) { 49 | return 50 | } 51 | csid := channel.Sid 52 | if !assert.NotEqual(t, "", csid, "Channel SID was empty") { 53 | return 54 | } 55 | 56 | channelList, err := ListIPChannels(client, ssid) 57 | if !assert.Nil(t, err, fmt.Sprintf("Failed to list channels: %v", err)) { 58 | return 59 | } 60 | channels, err := channelList.GetAllChannels() 61 | if !assert.Nil(t, err, fmt.Sprintf("Failed to get all channels: %v", err)) { 62 | return 63 | } 64 | 65 | found = false 66 | for _, c := range channels { 67 | if c.Sid == csid { 68 | found = true 69 | break 70 | } 71 | } 72 | if !assert.True(t, found, "Could not find channel") { 73 | return 74 | } 75 | 76 | // disables for now because I don't have keys to test with 77 | /* 78 | credential, err := NewIPCredential(client, "integration_cred", "gcm", false, "", "", "") 79 | if !assert.Nil(t, err, fmt.Sprintf("Failed to create credential: %v", err)) { 80 | return 81 | } 82 | credSid := credential.Sid 83 | if !assert.NotEqual(t, "", credSid, "Credential SID was empty") { 84 | return 85 | } 86 | */ 87 | 88 | role, err := NewIPRole(client, ssid, "integration_role", "channel", 89 | []string{PermissionSendMessage, PermissionEditOwnMessage}) 90 | if !assert.Nil(t, err, fmt.Sprintf("Failed to create role: %v", err)) { 91 | return 92 | } 93 | 94 | roleSid := role.Sid 95 | if !assert.NotEqual(t, "", roleSid, "Role SID was empty") { 96 | return 97 | } 98 | 99 | user, err := NewIPUser(client, ssid, "integration_user", "") 100 | if !assert.Nil(t, err, fmt.Sprintf("Failed to create user: %v", err)) { 101 | return 102 | } 103 | 104 | userSid := user.Sid 105 | if !assert.NotEqual(t, "", userSid, "User SID was empty") { 106 | return 107 | } 108 | 109 | member, err := AddIPMemberToChannel(client, ssid, csid, "integration_user", roleSid) 110 | if !assert.Nil(t, err, fmt.Sprintf("Failed to add member: %v", err)) { 111 | return 112 | } 113 | 114 | memberSid := member.Sid 115 | if !assert.NotEqual(t, "", memberSid, "Member SID was empty") { 116 | return 117 | } 118 | 119 | _, err = NewIPUser(client, ssid, "integration_user2", "") 120 | if !assert.Nil(t, err, fmt.Sprintf("Failed to create user: %v", err)) { 121 | return 122 | } 123 | _, err = AddIPMemberToChannel(client, ssid, csid, "integration_user2", roleSid) 124 | if !assert.Nil(t, err, fmt.Sprintf("Failed to add member: %v", err)) { 125 | return 126 | } 127 | 128 | memberList, err := ListIPMembers(client, ssid, csid) 129 | if !assert.Nil(t, err, fmt.Sprintf("Failed to add member: %v", err)) { 130 | return 131 | } 132 | 133 | members, err := memberList.GetAllMembers() 134 | if !assert.Nil(t, err, fmt.Sprintf("Failed to list members: %v", err)) { 135 | return 136 | } 137 | 138 | found = false 139 | for _, m := range members { 140 | if m.Sid == memberSid && m.Identity == member.Identity { 141 | found = true 142 | break 143 | } 144 | } 145 | 146 | if !assert.True(t, found, "Could not find member") { 147 | return 148 | } 149 | 150 | message, err := SendIPMessageToChannel(client, ssid, csid, "integration_user2", "testing integration") 151 | if !assert.Nil(t, err, fmt.Sprintf("Failed to send message: %v", err)) { 152 | return 153 | } 154 | if !assert.NotEqual(t, "", message.Sid, "Message SID was empty") { 155 | return 156 | } 157 | 158 | found = false 159 | messageList, err := ListIPMessages(client, ssid, csid) 160 | if !assert.Nil(t, err, fmt.Sprintf("Failed to list messages: %v", err)) { 161 | return 162 | } 163 | messages, err := messageList.GetAllMessages() 164 | if !assert.Nil(t, err, fmt.Sprintf("Failed to get all messages: %v", err)) { 165 | return 166 | } 167 | found = false 168 | for _, m := range messages { 169 | if m.Sid == message.Sid && m.Body == "testing integration" { 170 | found = true 171 | break 172 | } 173 | } 174 | if !assert.True(t, found, "Could not find message") { 175 | return 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /ip_member.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | // IPMember is a IP Messaging Member resource. 9 | type IPMember struct { 10 | Sid string `json:"sid"` 11 | AccountSid string `json:"account_sid"` 12 | ChannelSid string `json:"channel_sid"` 13 | ServiceSid string `json:"service_sid"` 14 | Identity string `json:"identity"` 15 | RoleSid *string `json:"role_sid"` 16 | DateCreated string `json:"date_created"` 17 | DateUpdated string `json:"date_updated"` 18 | URL string `json:"url"` 19 | } 20 | 21 | // IPMemberList gives the results for querying the set of members. Returns the first page 22 | // by default. 23 | type IPMemberList struct { 24 | Client Client 25 | Members []IPMember `json:"members"` 26 | Meta Meta `json:"meta"` 27 | } 28 | 29 | // AddIPMemberToChannel adds a member to a channel. 30 | func AddIPMemberToChannel(client *TwilioIPMessagingClient, serviceSid string, channelSid string, identity string, roleSid string) (*IPMember, error) { 31 | var member *IPMember 32 | 33 | params := url.Values{} 34 | params.Set("Identity", identity) 35 | if roleSid != "" { 36 | params.Set("RoleSid", roleSid) 37 | } 38 | 39 | res, err := client.post(params, "/Services/"+serviceSid+"/Channels/"+channelSid+"/Members.json") 40 | 41 | if err != nil { 42 | return member, err 43 | } 44 | 45 | member = new(IPMember) 46 | err = json.Unmarshal(res, member) 47 | 48 | return member, err 49 | } 50 | 51 | // GetIPChannelMember returns the specified IP Member in the channel. 52 | func GetIPChannelMember(client *TwilioIPMessagingClient, serviceSid, channelSid, sid string) (*IPMember, error) { 53 | var member *IPMember 54 | 55 | res, err := client.get(url.Values{}, "/Services/"+serviceSid+"/Channels/"+channelSid+"/Members/"+sid+".json") 56 | 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | member = new(IPMember) 62 | err = json.Unmarshal(res, member) 63 | 64 | return member, err 65 | } 66 | 67 | // RemoveIPMemberFromChannel removes the given member from the channel. 68 | func RemoveIPMemberFromChannel(client *TwilioIPMessagingClient, serviceSid, channelSid, sid string) error { 69 | return client.delete("/Services/" + serviceSid + "/Channels/" + channelSid + "/Members/" + sid) 70 | } 71 | 72 | // ListIPMembers returns the first page of members. 73 | func ListIPMembers(client *TwilioIPMessagingClient, serviceSid, channelSid string) (*IPMemberList, error) { 74 | var memberList *IPMemberList 75 | 76 | body, err := client.get(nil, "/Services/"+serviceSid+"/Channels/"+channelSid+"/Members.json") 77 | 78 | if err != nil { 79 | return memberList, err 80 | } 81 | 82 | memberList = new(IPMemberList) 83 | memberList.Client = client 84 | err = json.Unmarshal(body, memberList) 85 | 86 | return memberList, err 87 | } 88 | 89 | // GetMembers recturns the current page of members. 90 | func (c *IPMemberList) GetMembers() []IPMember { 91 | return c.Members 92 | } 93 | 94 | // GetAllMembers returns all of the members from all of the pages (from here forward). 95 | func (c *IPMemberList) GetAllMembers() ([]IPMember, error) { 96 | members := c.Members 97 | t := c 98 | 99 | for t.HasNextPage() { 100 | var err error 101 | t, err = t.NextPage() 102 | if err != nil { 103 | return nil, err 104 | } 105 | members = append(members, t.Members...) 106 | } 107 | return members, nil 108 | } 109 | 110 | // HasNextPage returns whether or not there is a next page of members. 111 | func (c *IPMemberList) HasNextPage() bool { 112 | return c.Meta.NextPageUri != "" 113 | } 114 | 115 | // NextPage returns the next page of members. 116 | func (c *IPMemberList) NextPage() (*IPMemberList, error) { 117 | if !c.HasNextPage() { 118 | return nil, Error{"No next page"} 119 | } 120 | 121 | return c.getPage(c.Meta.NextPageUri) 122 | } 123 | 124 | // HasPreviousPage indicates whether or not there is a previous page of results. 125 | func (c *IPMemberList) HasPreviousPage() bool { 126 | return c.Meta.PreviousPageUri != "" 127 | } 128 | 129 | // PreviousPage returns the previous page of members. 130 | func (c *IPMemberList) PreviousPage() (*IPMemberList, error) { 131 | if !c.HasPreviousPage() { 132 | return nil, Error{"No previous page"} 133 | } 134 | 135 | return c.getPage(c.Meta.NextPageUri) 136 | } 137 | 138 | // FirstPage returns the first page of members. 139 | func (c *IPMemberList) FirstPage() (*IPMemberList, error) { 140 | return c.getPage(c.Meta.FirstPageUri) 141 | } 142 | 143 | // LastPage returns the last page of members. 144 | func (c *IPMemberList) LastPage() (*IPMemberList, error) { 145 | return c.getPage(c.Meta.LastPageUri) 146 | } 147 | 148 | func (c *IPMemberList) getPage(uri string) (*IPMemberList, error) { 149 | var memberList *IPMemberList 150 | 151 | client := c.Client 152 | 153 | body, err := client.get(nil, uri) 154 | 155 | if err != nil { 156 | return memberList, err 157 | } 158 | 159 | memberList = new(IPMemberList) 160 | memberList.Client = client 161 | err = json.Unmarshal(body, memberList) 162 | 163 | return memberList, err 164 | } 165 | -------------------------------------------------------------------------------- /ip_message.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | // IPMessage is a IP Messaging Message resource. 9 | type IPMessage struct { 10 | Sid string `json:"sid"` 11 | AccountSid string `json:"account_sid"` 12 | ServiceSid string `json:"service_sid"` 13 | To string `json:"to"` // channel sid 14 | DateCreated string `json:"date_created"` 15 | DateUpdated string `json:"date_updated"` 16 | WasEdited bool `json:"was_edited"` 17 | From string `json:"from"` // identity 18 | Body string `json:"body"` 19 | URL string `json:"url"` 20 | } 21 | 22 | // IPMessageList gives the results for querying the set of messages. Returns the first page 23 | // by default. 24 | type IPMessageList struct { 25 | Client Client 26 | Messages []IPMessage `json:"messages"` 27 | Meta Meta `json:"meta"` 28 | } 29 | 30 | // SendIPMessageToChannel sends a message to a channel. 31 | func SendIPMessageToChannel(client *TwilioIPMessagingClient, serviceSid string, channelSid string, from string, body string) (*IPMessage, error) { 32 | var message *IPMessage 33 | 34 | params := url.Values{} 35 | params.Set("Body", body) 36 | if from != "" { 37 | params.Set("From", from) 38 | } 39 | 40 | res, err := client.post(params, "/Services/"+serviceSid+"/Channels/"+channelSid+"/Messages.json") 41 | 42 | if err != nil { 43 | return message, err 44 | } 45 | 46 | message = new(IPMessage) 47 | err = json.Unmarshal(res, message) 48 | 49 | return message, err 50 | } 51 | 52 | // GetIPChannelMessage returns the specified IP Message in the channel. 53 | func GetIPChannelMessage(client *TwilioIPMessagingClient, serviceSid, channelSid, sid string) (*IPMessage, error) { 54 | var message *IPMessage 55 | 56 | res, err := client.get(url.Values{}, "/Services/"+serviceSid+"/Channels/"+channelSid+"/Messages/"+sid+".json") 57 | 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | message = new(IPMessage) 63 | err = json.Unmarshal(res, message) 64 | 65 | return message, err 66 | } 67 | 68 | // ListIPMessages returns the first page of messages for a channel. 69 | func ListIPMessages(client *TwilioIPMessagingClient, serviceSid, channelSid string) (*IPMessageList, error) { 70 | var messageList *IPMessageList 71 | 72 | body, err := client.get(nil, "/Services/"+serviceSid+"/Channels/"+channelSid+"/Messages.json") 73 | 74 | if err != nil { 75 | return messageList, err 76 | } 77 | 78 | messageList = new(IPMessageList) 79 | messageList.Client = client 80 | err = json.Unmarshal(body, messageList) 81 | 82 | return messageList, err 83 | } 84 | 85 | // GetMessages recturns the current page of messages. 86 | func (c *IPMessageList) GetMessages() []IPMessage { 87 | return c.Messages 88 | } 89 | 90 | // GetAllMessages returns all of the messages from all of the pages (from here forward). 91 | func (c *IPMessageList) GetAllMessages() ([]IPMessage, error) { 92 | messages := c.Messages 93 | t := c 94 | 95 | for t.HasNextPage() { 96 | var err error 97 | t, err = t.NextPage() 98 | if err != nil { 99 | return nil, err 100 | } 101 | messages = append(messages, t.Messages...) 102 | } 103 | return messages, nil 104 | } 105 | 106 | // HasNextPage returns whether or not there is a next page of messages. 107 | func (c *IPMessageList) HasNextPage() bool { 108 | return c.Meta.NextPageUri != "" 109 | } 110 | 111 | // NextPage returns the next page of messages. 112 | func (c *IPMessageList) NextPage() (*IPMessageList, error) { 113 | if !c.HasNextPage() { 114 | return nil, Error{"No next page"} 115 | } 116 | 117 | return c.getPage(c.Meta.NextPageUri) 118 | } 119 | 120 | // HasPreviousPage indicates whether or not there is a previous page of results. 121 | func (c *IPMessageList) HasPreviousPage() bool { 122 | return c.Meta.PreviousPageUri != "" 123 | } 124 | 125 | // PreviousPage returns the previous page of messages. 126 | func (c *IPMessageList) PreviousPage() (*IPMessageList, error) { 127 | if !c.HasPreviousPage() { 128 | return nil, Error{"No previous page"} 129 | } 130 | 131 | return c.getPage(c.Meta.NextPageUri) 132 | } 133 | 134 | // FirstPage returns the first page of messages. 135 | func (c *IPMessageList) FirstPage() (*IPMessageList, error) { 136 | return c.getPage(c.Meta.FirstPageUri) 137 | } 138 | 139 | // LastPage returns the last page of messages. 140 | func (c *IPMessageList) LastPage() (*IPMessageList, error) { 141 | return c.getPage(c.Meta.LastPageUri) 142 | } 143 | 144 | func (c *IPMessageList) getPage(uri string) (*IPMessageList, error) { 145 | var messageList *IPMessageList 146 | 147 | client := c.Client 148 | 149 | body, err := client.get(nil, uri) 150 | 151 | if err != nil { 152 | return messageList, err 153 | } 154 | 155 | messageList = new(IPMessageList) 156 | messageList.Client = client 157 | err = json.Unmarshal(body, messageList) 158 | 159 | return messageList, err 160 | } 161 | -------------------------------------------------------------------------------- /ip_role.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | // IPRole is a IP Messaging Role resource. 9 | type IPRole struct { 10 | Sid string `json:"sid"` 11 | AccountSid string `json:"account_sid"` 12 | ServiceSid string `json:"service_sid"` 13 | FriendlyName string `json:"friendly_name"` 14 | Type string `json:"type"` 15 | Permissions []string `json:"permissions"` 16 | DateCreated string `json:"date_created"` 17 | DateUpdated string `json:"date_updated"` 18 | URL string `json:"url"` 19 | } 20 | 21 | // IPRoleList gives the results for querying the set of roles. Returns the first page 22 | // by default. 23 | type IPRoleList struct { 24 | Client *TwilioIPMessagingClient 25 | Roles []IPRole `json:"roles"` 26 | Meta Meta `json:"meta"` 27 | } 28 | 29 | // Permissions allowed for IP Roles. 30 | const ( 31 | PermissionCreateChannel = "createChannel" 32 | PermissionJoinChannel = "joinChannel" 33 | PermissionDestroyChannel = "destroyChannel" 34 | PermissionInviteMember = "inviteMember" 35 | PermissionRemoveMember = "removeMember" 36 | PermissionEditChannelName = "editChannelName" 37 | PermissionEditChannelAttributes = "editChannelAttributes" 38 | PermissionAddMember = "addMember" 39 | PermissionEditAnyMessage = "editAnyMessage" 40 | PermissionDeleteAnyMessage = "deleteAnyMessage" 41 | PermissionSendMessage = "sendMessage" 42 | PermissionLeaveChannel = "leaveChannel" 43 | PermissionEditOwnMessage = "editOwnMessage" 44 | PermissionDeleteOwnMessage = "deleteOwnMessage" 45 | ) 46 | 47 | // NewIPRole creates a new IP Messaging Role. 48 | // kind should be "channel" or "service". 49 | // permissions should be a subset of the permissions consts above. 50 | func NewIPRole(client *TwilioIPMessagingClient, serviceSid string, friendlyName string, kind string, permissions []string) (*IPRole, error) { 51 | var role *IPRole 52 | 53 | params := url.Values{} 54 | params.Set("FriendlyName", friendlyName) 55 | params.Set("Type", kind) 56 | if permissions != nil { 57 | for _, p := range permissions { 58 | params.Add("Permission", p) 59 | } 60 | } 61 | 62 | res, err := client.post(params, "/Services/"+serviceSid+"/Roles.json") 63 | 64 | if err != nil { 65 | return role, err 66 | } 67 | 68 | role = new(IPRole) 69 | err = json.Unmarshal(res, role) 70 | 71 | return role, err 72 | } 73 | 74 | // GetIPRole returns information on the specified role. 75 | func GetIPRole(client *TwilioIPMessagingClient, serviceSid, sid string) (*IPRole, error) { 76 | var role *IPRole 77 | 78 | res, err := client.get(url.Values{}, "/Services/"+serviceSid+"/Roles/"+sid+".json") 79 | 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | role = new(IPRole) 85 | err = json.Unmarshal(res, role) 86 | 87 | return role, err 88 | } 89 | 90 | // DeleteIPRole deletes the given IP Role. 91 | func DeleteIPRole(client *TwilioIPMessagingClient, serviceSid, sid string) error { 92 | return client.delete("/Services/" + serviceSid + "/Roles/" + sid) 93 | } 94 | 95 | // UpdateIPRole updates an existing IP Messaging Role. 96 | func UpdateIPRole(client *TwilioIPMessagingClient, serviceSid string, sid string, friendlyName string, kind string, permissions []string) (*IPRole, error) { 97 | var role *IPRole 98 | 99 | params := url.Values{} 100 | params.Set("FriendlyName", friendlyName) 101 | params.Set("Type", kind) 102 | if permissions != nil { 103 | for _, p := range permissions { 104 | params.Add("Permission", p) 105 | } 106 | } 107 | 108 | res, err := client.post(params, "/Services/"+serviceSid+"/Roles/"+sid+".json") 109 | 110 | if err != nil { 111 | return role, err 112 | } 113 | 114 | role = new(IPRole) 115 | err = json.Unmarshal(res, role) 116 | 117 | return role, err 118 | } 119 | 120 | // ListIPRoles returns the first page of roles. 121 | func ListIPRoles(client *TwilioIPMessagingClient, serviceSid string) (*IPRoleList, error) { 122 | var roleList *IPRoleList 123 | 124 | body, err := client.get(nil, "/Services/"+serviceSid+"/Roles.json") 125 | 126 | if err != nil { 127 | return roleList, err 128 | } 129 | 130 | roleList = new(IPRoleList) 131 | roleList.Client = client 132 | err = json.Unmarshal(body, roleList) 133 | 134 | return roleList, err 135 | } 136 | 137 | // GetRoles returns the current page of roles. 138 | func (s *IPRoleList) GetRoles() []IPRole { 139 | return s.Roles 140 | } 141 | 142 | // GetAllRoles returns all of the roles from all of the pages (from here forward). 143 | func (s *IPRoleList) GetAllRoles() ([]IPRole, error) { 144 | roles := s.Roles 145 | t := s 146 | 147 | for t.HasNextPage() { 148 | var err error 149 | t, err = t.NextPage() 150 | if err != nil { 151 | return nil, err 152 | } 153 | roles = append(roles, t.Roles...) 154 | } 155 | return roles, nil 156 | } 157 | 158 | // HasNextPage returns whether or not there is a next page of roles. 159 | func (s *IPRoleList) HasNextPage() bool { 160 | return s.Meta.NextPageUri != "" 161 | } 162 | 163 | // NextPage returns the next page of roles. 164 | func (s *IPRoleList) NextPage() (*IPRoleList, error) { 165 | if !s.HasNextPage() { 166 | return nil, Error{"No next page"} 167 | } 168 | 169 | return s.getPage(s.Meta.NextPageUri) 170 | } 171 | 172 | // HasPreviousPage indicates whether or not there is a previous page of results. 173 | func (s *IPRoleList) HasPreviousPage() bool { 174 | return s.Meta.PreviousPageUri != "" 175 | } 176 | 177 | // PreviousPage returns the previous page of roles. 178 | func (s *IPRoleList) PreviousPage() (*IPRoleList, error) { 179 | if !s.HasPreviousPage() { 180 | return nil, Error{"No previous page"} 181 | } 182 | 183 | return s.getPage(s.Meta.NextPageUri) 184 | } 185 | 186 | // FirstPage returns the first page of roles. 187 | func (s *IPRoleList) FirstPage() (*IPRoleList, error) { 188 | return s.getPage(s.Meta.FirstPageUri) 189 | } 190 | 191 | // LastPage returns the last page of roles. 192 | func (s *IPRoleList) LastPage() (*IPRoleList, error) { 193 | return s.getPage(s.Meta.LastPageUri) 194 | } 195 | 196 | func (s *IPRoleList) getPage(uri string) (*IPRoleList, error) { 197 | var roleList *IPRoleList 198 | 199 | client := s.Client 200 | 201 | body, err := client.get(nil, uri) 202 | 203 | if err != nil { 204 | return roleList, err 205 | } 206 | 207 | roleList = new(IPRoleList) 208 | roleList.Client = client 209 | err = json.Unmarshal(body, roleList) 210 | 211 | return roleList, err 212 | } 213 | -------------------------------------------------------------------------------- /ip_service.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math" 7 | "net/url" 8 | "time" 9 | ) 10 | 11 | // IPService is a IP Messaging Service resource. 12 | type IPService struct { 13 | Sid string `json:"sid"` 14 | AccountSid string `json:"account_sid"` 15 | FriendlyName string `json:"friendly_name"` 16 | DateCreated string `json:"date_created"` 17 | DateUpdated string `json:"date_updated"` 18 | DefaultServiceRoleSid string `json:"default_service_role_sid"` 19 | DefaultChannelRoleSid string `json:"default_channel_role_sid"` 20 | TypingIndicatorTimeout uint `json:"typing_indicator_timeout"` 21 | Webhooks map[string]string `json:"webhooks"` 22 | URL string `json:"url"` 23 | Links map[string]string `json:"links"` 24 | } 25 | 26 | // Meta is a metadata type for the IP messaging services. 27 | type Meta struct { 28 | Start int `json:"start"` 29 | Total int `json:"total"` 30 | NumPages int `json:"num_pages"` 31 | Page int `json:"page"` 32 | PageSize int `json:"page_size"` 33 | End int `json:"end"` 34 | Uri string `json:"uri"` 35 | FirstPageUri string `json:"first_page_uri"` 36 | LastPageUri string `json:"last_page_uri"` 37 | NextPageUri string `json:"next_page_uri"` 38 | PreviousPageUri string `json:"previous_page_uri"` 39 | Key string `json:"key"` 40 | } 41 | 42 | // IPServiceList gives the results for querying the set of services. Returns the first page 43 | // by default. 44 | type IPServiceList struct { 45 | Client Client 46 | Services []IPService `json:"services"` 47 | Meta Meta `json:"meta"` 48 | } 49 | 50 | // Webhooks available for services to specify 51 | const ( 52 | WebhookOnMessageSend = "Webhooks.OnMessageSend" 53 | WebhookOnMessageRemove = "Webhooks.OnMessageRemove" 54 | WebhookOnMessageUpdate = "Webhooks.OnMessageUpdate" 55 | WebhookOnChannelAdd = "Webhooks.OnChannelAdd" 56 | WebhookOnChannelUpdate = "Webhooks.OnChannelUpdate" 57 | WebhookOnChannelDestroy = "Webhooks.OnChannelDestroy" 58 | WebhookOnMemberAdd = "Webhooks.OnMemberAdd" 59 | WebhookOnMemberRemove = "Webhooks.OnMemberRemove" 60 | ) 61 | 62 | // Webhooks are used to define push webhooks for an IP service. 63 | type Webhooks map[string]string 64 | 65 | // NewWebhooks creates a new, empty set of web hooks. 66 | func NewWebhooks() Webhooks { 67 | return Webhooks(make(map[string]string)) 68 | } 69 | 70 | // Add adds a new webhook. The name should be one of the Webhook* exported values. 71 | // Method is the HTTP method (e.g., "POST"). Format should be "xml" or "json". 72 | func (w Webhooks) Add(name, method, format, url string) { 73 | w[name+".Method"] = method 74 | w[name+".Format"] = format 75 | w[name+".Url"] = url 76 | } 77 | 78 | func durationToISO8601(d time.Duration) (string, error) { 79 | if d > time.Hour { 80 | return "", fmt.Errorf("Duration is too long: %v", d) 81 | } 82 | minutes := int(math.Floor(d.Minutes())) 83 | seconds := int(math.Floor(d.Minutes()-float64(minutes)) * 60.0) 84 | return fmt.Sprintf("PT%dM%dS", minutes, seconds), nil 85 | } 86 | 87 | // NewIPService creates a new IP Messaging Service. 88 | func NewIPService(client *TwilioIPMessagingClient, friendlyName string, defaultServiceRoleSid string, defaultChannelRoleSid string, 89 | typingIndicatorTimeout time.Duration, webhooks Webhooks) (*IPService, error) { 90 | 91 | timeout, err := durationToISO8601(typingIndicatorTimeout) 92 | if err != nil { 93 | return nil, err 94 | } 95 | 96 | var service *IPService 97 | 98 | params := url.Values{} 99 | params.Set("FriendlyName", friendlyName) 100 | params.Set("DefaultServiceRoleSid", defaultServiceRoleSid) 101 | params.Set("DefaultChannelRoleSid", defaultChannelRoleSid) 102 | params.Set("TypingIndicatonTimeout", timeout) 103 | if webhooks != nil { 104 | for k, v := range webhooks { 105 | params.Set(k, v) 106 | } 107 | } 108 | 109 | res, err := client.post(params, "/Services.json") 110 | 111 | if err != nil { 112 | return service, err 113 | } 114 | 115 | service = new(IPService) 116 | err = json.Unmarshal(res, service) 117 | 118 | return service, err 119 | } 120 | 121 | // GetIPService returns information on the specified service. 122 | func GetIPService(client *TwilioIPMessagingClient, sid string) (*IPService, error) { 123 | var service *IPService 124 | 125 | res, err := client.get(url.Values{}, "/Services/"+sid+".json") 126 | 127 | if err != nil { 128 | return nil, err 129 | } 130 | 131 | service = new(IPService) 132 | err = json.Unmarshal(res, service) 133 | 134 | return service, err 135 | } 136 | 137 | // DeleteIPService deletes the given IP Service. 138 | func DeleteIPService(client *TwilioIPMessagingClient, sid string) error { 139 | return client.delete("/Services/" + sid) 140 | } 141 | 142 | // UpdateIPService updates an existing IP Messaging Service. 143 | func UpdateIPService(client *TwilioIPMessagingClient, sid string, friendlyName string, defaultServiceRoleSid string, defaultChannelRoleSid string, 144 | typingIndicatorTimeout time.Duration, webhooks Webhooks) (*IPService, error) { 145 | 146 | timeout, err := durationToISO8601(typingIndicatorTimeout) 147 | if err != nil { 148 | return nil, err 149 | } 150 | 151 | var service *IPService 152 | 153 | params := url.Values{} 154 | params.Set("FriendlyName", friendlyName) 155 | params.Set("DefaultServiceRoleSid", defaultServiceRoleSid) 156 | params.Set("DefaultChannelRoleSid", defaultChannelRoleSid) 157 | params.Set("TypingIndicatonTimeout", timeout) 158 | for k, v := range webhooks { 159 | params.Set(k, v) 160 | } 161 | 162 | res, err := client.post(params, "/Services/"+sid+".json") 163 | 164 | if err != nil { 165 | return service, err 166 | } 167 | 168 | service = new(IPService) 169 | err = json.Unmarshal(res, service) 170 | 171 | return service, err 172 | } 173 | 174 | // ListIPServices returns the first page of services. 175 | func ListIPServices(client *TwilioIPMessagingClient) (*IPServiceList, error) { 176 | var serviceList *IPServiceList 177 | 178 | body, err := client.get(nil, "/Services.json") 179 | 180 | if err != nil { 181 | return serviceList, err 182 | } 183 | 184 | serviceList = new(IPServiceList) 185 | serviceList.Client = client 186 | err = json.Unmarshal(body, serviceList) 187 | 188 | return serviceList, err 189 | } 190 | 191 | // GetServices returns the current page of services. 192 | func (s *IPServiceList) GetServices() []IPService { 193 | return s.Services 194 | } 195 | 196 | // GetAllServices returns all of the services from all of the pages (from here forward). 197 | func (s *IPServiceList) GetAllServices() ([]IPService, error) { 198 | services := s.Services 199 | t := s 200 | 201 | for t.HasNextPage() { 202 | var err error 203 | t, err = t.NextPage() 204 | if err != nil { 205 | return nil, err 206 | } 207 | services = append(services, t.Services...) 208 | } 209 | return services, nil 210 | } 211 | 212 | // HasNextPage returns whether or not there is a next page of services. 213 | func (s *IPServiceList) HasNextPage() bool { 214 | return s.Meta.NextPageUri != "" 215 | } 216 | 217 | // NextPage returns the next page of services. 218 | func (s *IPServiceList) NextPage() (*IPServiceList, error) { 219 | if !s.HasNextPage() { 220 | return nil, Error{"No next page"} 221 | } 222 | 223 | return s.getPage(s.Meta.NextPageUri) 224 | } 225 | 226 | // HasPreviousPage indicates whether or not there is a previous page of results. 227 | func (s *IPServiceList) HasPreviousPage() bool { 228 | return s.Meta.PreviousPageUri != "" 229 | } 230 | 231 | // PreviousPage returns the previous page of services. 232 | func (s *IPServiceList) PreviousPage() (*IPServiceList, error) { 233 | if !s.HasPreviousPage() { 234 | return nil, Error{"No previous page"} 235 | } 236 | 237 | return s.getPage(s.Meta.NextPageUri) 238 | } 239 | 240 | // FirstPage returns the first page of services. 241 | func (s *IPServiceList) FirstPage() (*IPServiceList, error) { 242 | return s.getPage(s.Meta.FirstPageUri) 243 | } 244 | 245 | // LastPage returns the last page of services. 246 | func (s *IPServiceList) LastPage() (*IPServiceList, error) { 247 | return s.getPage(s.Meta.LastPageUri) 248 | } 249 | 250 | func (s *IPServiceList) getPage(uri string) (*IPServiceList, error) { 251 | var serviceList *IPServiceList 252 | 253 | client := s.Client 254 | 255 | body, err := client.get(nil, uri) 256 | 257 | if err != nil { 258 | return serviceList, err 259 | } 260 | 261 | serviceList = new(IPServiceList) 262 | serviceList.Client = client 263 | err = json.Unmarshal(body, serviceList) 264 | 265 | return serviceList, err 266 | } 267 | -------------------------------------------------------------------------------- /ip_user.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | // IPUser is a IP Messaging User resource. 9 | type IPUser struct { 10 | Sid string `json:"sid"` 11 | AccountSid string `json:"account_sid"` 12 | ServiceSid string `json:"service_sid"` 13 | RoleSid string `json:"role_sid"` 14 | Identity string `json:"identity"` 15 | DateCreated string `json:"date_created"` 16 | DateUpdated string `json:"date_updated"` 17 | URL string `json:"url"` 18 | } 19 | 20 | // IPUserList gives the results for querying the set of users. Returns the first page 21 | // by default. 22 | type IPUserList struct { 23 | Client *TwilioIPMessagingClient 24 | Users []IPUser `json:"users"` 25 | Meta Meta `json:"meta"` 26 | } 27 | 28 | // NewIPUser creates a new IP Messaging User. 29 | func NewIPUser(client *TwilioIPMessagingClient, serviceSid string, identity string, roleSid string) (*IPUser, error) { 30 | var user *IPUser 31 | 32 | params := url.Values{} 33 | params.Set("Identity", identity) 34 | params.Set("RoleSid", roleSid) 35 | 36 | res, err := client.post(params, "/Services/"+serviceSid+"/Users.json") 37 | 38 | if err != nil { 39 | return user, err 40 | } 41 | 42 | user = new(IPUser) 43 | err = json.Unmarshal(res, user) 44 | 45 | return user, err 46 | } 47 | 48 | // GetIPUser returns information on the specified user. 49 | func GetIPUser(client *TwilioIPMessagingClient, serviceSid, sid string) (*IPUser, error) { 50 | var user *IPUser 51 | 52 | res, err := client.get(url.Values{}, "/Services/"+serviceSid+"/Users/"+sid+".json") 53 | 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | user = new(IPUser) 59 | err = json.Unmarshal(res, user) 60 | 61 | return user, err 62 | } 63 | 64 | // DeleteIPUser deletes the given IP user. 65 | func DeleteIPUser(client *TwilioIPMessagingClient, serviceSid, sid string) error { 66 | return client.delete("/Services/" + serviceSid + "/Users/" + sid) 67 | } 68 | 69 | // UpdateIPUser updates an existing IP Messaging user. 70 | func UpdateIPUser(client *TwilioIPMessagingClient, serviceSid string, sid string, identity string, roleSid string) (*IPUser, error) { 71 | var user *IPUser 72 | 73 | params := url.Values{} 74 | params.Set("Identity", identity) 75 | params.Set("RoleSid", roleSid) 76 | 77 | res, err := client.post(params, "/Services/"+serviceSid+"/Users/"+sid+".json") 78 | 79 | if err != nil { 80 | return user, err 81 | } 82 | 83 | user = new(IPUser) 84 | err = json.Unmarshal(res, user) 85 | 86 | return user, err 87 | } 88 | 89 | // ListIPUsers returns the first page of users. 90 | func ListIPUsers(client *TwilioIPMessagingClient, serviceSid string) (*IPUserList, error) { 91 | var userList *IPUserList 92 | 93 | body, err := client.get(nil, "/Services/"+serviceSid+"/Users.json") 94 | 95 | if err != nil { 96 | return userList, err 97 | } 98 | 99 | userList = new(IPUserList) 100 | userList.Client = client 101 | err = json.Unmarshal(body, userList) 102 | 103 | return userList, err 104 | } 105 | 106 | // GetUsers returns the current page of users. 107 | func (s *IPUserList) GetUsers() []IPUser { 108 | return s.Users 109 | } 110 | 111 | // GetAllUsers returns all of the users from all of the pages (from here forward). 112 | func (s *IPUserList) GetAllUsers() ([]IPUser, error) { 113 | users := s.Users 114 | t := s 115 | 116 | for t.HasNextPage() { 117 | var err error 118 | t, err = t.NextPage() 119 | if err != nil { 120 | return nil, err 121 | } 122 | users = append(users, t.Users...) 123 | } 124 | return users, nil 125 | } 126 | 127 | // HasNextPage returns whether or not there is a next page of users. 128 | func (s *IPUserList) HasNextPage() bool { 129 | return s.Meta.NextPageUri != "" 130 | } 131 | 132 | // NextPage returns the next page of users. 133 | func (s *IPUserList) NextPage() (*IPUserList, error) { 134 | if !s.HasNextPage() { 135 | return nil, Error{"No next page"} 136 | } 137 | 138 | return s.getPage(s.Meta.NextPageUri) 139 | } 140 | 141 | // HasPreviousPage indicates whether or not there is a previous page of results. 142 | func (s *IPUserList) HasPreviousPage() bool { 143 | return s.Meta.PreviousPageUri != "" 144 | } 145 | 146 | // PreviousPage returns the previous page of users. 147 | func (s *IPUserList) PreviousPage() (*IPUserList, error) { 148 | if !s.HasPreviousPage() { 149 | return nil, Error{"No previous page"} 150 | } 151 | 152 | return s.getPage(s.Meta.NextPageUri) 153 | } 154 | 155 | // FirstPage returns the first page of users. 156 | func (s *IPUserList) FirstPage() (*IPUserList, error) { 157 | return s.getPage(s.Meta.FirstPageUri) 158 | } 159 | 160 | // LastPage returns the last page of users. 161 | func (s *IPUserList) LastPage() (*IPUserList, error) { 162 | return s.getPage(s.Meta.LastPageUri) 163 | } 164 | 165 | func (s *IPUserList) getPage(uri string) (*IPUserList, error) { 166 | var userList *IPUserList 167 | 168 | client := s.Client 169 | 170 | body, err := client.get(nil, uri) 171 | 172 | if err != nil { 173 | return userList, err 174 | } 175 | 176 | userList = new(IPUserList) 177 | userList.Client = client 178 | err = json.Unmarshal(body, userList) 179 | 180 | return userList, err 181 | } 182 | -------------------------------------------------------------------------------- /message.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | type Message struct { 9 | Sid string `json:"sid"` 10 | DateCreated string `json:"date_created"` 11 | DateUpdated string `json:"date_updated"` 12 | DateSent string `json:"date_sent"` 13 | AccountSid string `json:"account_sid"` 14 | From string `json:"from"` 15 | To string `json:"to"` 16 | Body string `json:"body"` 17 | NumSegments string `json:"num_segments"` 18 | Status string `json:"status"` 19 | Direction string `json:"direction"` 20 | Price string `json:"price"` 21 | PriceUnit string `json:"price_unit"` 22 | ApiVersion string `json:"api_version"` 23 | Uri string `json:"uri"` 24 | } 25 | 26 | func NewMessage(client Client, from string, to string, content ...Optional) (*Message, error) { 27 | var message *Message 28 | 29 | params := url.Values{} 30 | params.Set("From", from) 31 | params.Set("To", to) 32 | 33 | for _, optional := range content { 34 | param, value := optional.GetParam() 35 | 36 | if param != "Body" && param != "MediaUrl" && param != "StatusCallback" && param != "ApplicationSid" && param != "MessagingServiceSid" { 37 | return nil, Error{"Only allowed params are Body, MediaUrl, StatusCallback, ApplicationSid, MessagingServiceSid"} 38 | } 39 | 40 | params.Set(param, value) 41 | } 42 | 43 | if params.Get("Body") == "" && params.Get("MediaUrl") == "" { 44 | return nil, Error{"Must have at least a Body or MediaUrl"} 45 | } 46 | 47 | res, err := client.post(params, "/Messages.json") 48 | 49 | if err != nil { 50 | return message, err 51 | } 52 | 53 | message = new(Message) 54 | err = json.Unmarshal(res, message) 55 | 56 | return message, err 57 | } 58 | 59 | func GetMessage(client Client, sid string) (*Message, error) { 60 | var message *Message 61 | 62 | res, err := client.get(url.Values{}, "/Messages/"+sid+".json") 63 | 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | message = new(Message) 69 | err = json.Unmarshal(res, message) 70 | 71 | return message, err 72 | } 73 | -------------------------------------------------------------------------------- /message_integration_test.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestIntegrationMessageList(t *testing.T) { 9 | CheckTestEnv(t) 10 | 11 | client := NewClient(API_KEY, API_TOKEN) 12 | 13 | messageList, err := GetMessageList(client) 14 | 15 | if assert.Nil(t, err, "Failed to retrieve message list") { 16 | messages := messageList.Messages 17 | assert.NotNil(t, messages, "Failed to retrieve messages") 18 | } 19 | } 20 | 21 | func TestIntegrationSendSMS(t *testing.T) { 22 | /* /Messages endpoint is currently not recognized by Test Credentials */ 23 | /* CheckTestEnv(t) */ 24 | 25 | /* client := NewClient(TEST_KEY, TEST_TOKEN) */ 26 | 27 | /* message, err := NewMessage(client, TEST_FROM_NUMBER, TO_NUMBER, Body("Test Message")) */ 28 | 29 | /* if assert.Nil(t, err, "Failed to Send SMS") { */ 30 | /* assert.Equal(t, message.Status, "queued", "Sending SMS failed, status: " + message.Status) */ 31 | /* } */ 32 | t.Skip() 33 | } 34 | 35 | func TestIntegrationMessageListNextPage(t *testing.T) { 36 | CheckTestEnv(t) 37 | 38 | client := NewClient(API_KEY, API_TOKEN) 39 | 40 | messageList, err := GetMessageList(client) 41 | 42 | if assert.Nil(t, err, "Failed to retrieve message list") { 43 | nextPageMessageList, err := messageList.NextPage() 44 | 45 | if assert.Nil(t, err, "Failed to retrieve message list") { 46 | assert.Equal(t, nextPageMessageList.Page, 1, "Page incorrect on next page") 47 | } 48 | } 49 | } 50 | 51 | func TestIntegrationGetMessage(t *testing.T) { 52 | CheckTestEnv(t) 53 | 54 | client := NewClient(API_KEY, API_TOKEN) 55 | 56 | messageList, err := GetMessageList(client) 57 | 58 | if assert.Nil(t, err, "Failed to retrieve message list") { 59 | messageSid := messageList.Messages[0].Sid 60 | message, err := GetMessage(client, messageSid) 61 | 62 | if assert.Nil(t, err, "Failed to retrieve message") { 63 | assert.Equal(t, message.Sid, messageSid, "Message was invalid") 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /message_list.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | type MessageList struct { 9 | Client Client 10 | Start int `json:"start"` 11 | Total int `json:"total"` 12 | NumPages int `json:"num_pages"` 13 | Page int `json:"page"` 14 | PageSize int `json:"page_size"` 15 | End int `json:"end"` 16 | Uri string `json:"uri"` 17 | FirstPageUri string `json:"first_page_uri"` 18 | LastPageUri string `json:"last_page_uri"` 19 | NextPageUri string `json:"next_page_uri"` 20 | PreviousPageUri string `json":previous_page_uri"` 21 | Messages []Message `json:"sms_messages"` 22 | } 23 | 24 | func GetMessageList(client Client, optionals ...Optional) (*MessageList, error) { 25 | var messageList *MessageList 26 | 27 | params := url.Values{} 28 | 29 | for _, optional := range optionals { 30 | param, value := optional.GetParam() 31 | params.Set(param, value) 32 | } 33 | 34 | body, err := client.get(params, "/SMS/Messages.json") 35 | 36 | if err != nil { 37 | return messageList, err 38 | } 39 | 40 | messageList = new(MessageList) 41 | messageList.Client = client 42 | err = json.Unmarshal(body, messageList) 43 | 44 | return messageList, err 45 | } 46 | 47 | func (m *MessageList) GetMessages() []Message { 48 | return m.Messages 49 | } 50 | 51 | func (currentMessageList *MessageList) HasNextPage() bool { 52 | return currentMessageList.NextPageUri != "" 53 | } 54 | 55 | func (currentMessageList *MessageList) NextPage() (*MessageList, error) { 56 | if !currentMessageList.HasNextPage() { 57 | return nil, Error{"No next page"} 58 | } 59 | 60 | return currentMessageList.getPage(currentMessageList.NextPageUri) 61 | } 62 | 63 | func (currentMessageList *MessageList) HasPreviousPage() bool { 64 | return currentMessageList.PreviousPageUri != "" 65 | } 66 | 67 | func (currentMessageList *MessageList) PreviousPage() (*MessageList, error) { 68 | if !currentMessageList.HasPreviousPage() { 69 | return nil, Error{"No previous page"} 70 | } 71 | 72 | return currentMessageList.getPage(currentMessageList.NextPageUri) 73 | } 74 | 75 | func (currentMessageList *MessageList) FirstPage() (*MessageList, error) { 76 | return currentMessageList.getPage(currentMessageList.FirstPageUri) 77 | } 78 | 79 | func (currentMessageList *MessageList) LastPage() (*MessageList, error) { 80 | return currentMessageList.getPage(currentMessageList.LastPageUri) 81 | } 82 | 83 | func (currentMessageList *MessageList) getPage(uri string) (*MessageList, error) { 84 | var messageList *MessageList 85 | 86 | client := currentMessageList.Client 87 | 88 | body, err := client.get(nil, uri) 89 | 90 | if err != nil { 91 | return messageList, err 92 | } 93 | 94 | messageList = new(MessageList) 95 | messageList.Client = client 96 | err = json.Unmarshal(body, messageList) 97 | 98 | return messageList, err 99 | } 100 | -------------------------------------------------------------------------------- /message_test.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | var testMessage = Message{ 12 | Sid: "testsid", 13 | DateCreated: "2013-05-11", 14 | DateUpdated: "2013-05-11", 15 | DateSent: "2013-05-11", 16 | AccountSid: "AC3TestAccount", 17 | From: "+15555555555", 18 | To: "+16666666666", 19 | Body: "TestBody", 20 | NumSegments: "1", 21 | Status: "queued", 22 | Direction: "outbound-api", 23 | Price: "4", 24 | PriceUnit: "dollars", 25 | ApiVersion: "2008-04-01", 26 | Uri: "/2010-04-01/Accounts/AC3TestAccount/Messages/testsid.json", 27 | } 28 | 29 | func TestNewMessage(t *testing.T) { 30 | client := new(MockClient) 31 | 32 | messageJson, _ := json.Marshal(testMessage) 33 | 34 | params := url.Values{} 35 | params.Set("From", "6666666666") 36 | params.Set("To", "5555555555") 37 | params.Set("Body", "TestBody") 38 | 39 | client.On("post", params, "/Messages.json").Return(messageJson, nil) 40 | 41 | message, err := NewMessage(client, "6666666666", "5555555555", Body("TestBody")) 42 | 43 | client.Mock.AssertExpectations(t) 44 | 45 | if assert.Nil(t, err, "Error unmarshaling message") { 46 | assert.Equal(t, message.Sid, "testsid", "Message malformed") 47 | } 48 | } 49 | 50 | func TestGetMessage(t *testing.T) { 51 | client := new(MockClient) 52 | 53 | messageJson, _ := json.Marshal(testMessage) 54 | 55 | client.On("get", url.Values{}, "/Messages/testsid.json").Return(messageJson, nil) 56 | 57 | message, err := GetMessage(client, "testsid") 58 | 59 | client.Mock.AssertExpectations(t) 60 | 61 | if assert.Nil(t, err, "Error unmarshaling message") { 62 | assert.Equal(t, message.Sid, "testsid", "Message malformed") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /mock_client.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | import ( 4 | "net/url" 5 | 6 | "github.com/stretchr/testify/mock" 7 | ) 8 | 9 | type MockClient struct { 10 | mock.Mock 11 | } 12 | 13 | func (client *MockClient) AccountSid() string { 14 | return "AC3FakeClient" 15 | } 16 | 17 | func (client *MockClient) AuthToken() string { 18 | return "98h4hfaketoken" 19 | } 20 | 21 | func (client *MockClient) RootUrl() string { 22 | return "http://test.com/fake" 23 | } 24 | 25 | func (client *MockClient) get(params url.Values, uri string) ([]byte, error) { 26 | args := client.Mock.Called(params, uri) 27 | return args.Get(0).([]byte), args.Error(1) 28 | } 29 | 30 | func (client *MockClient) post(params url.Values, uri string) ([]byte, error) { 31 | args := client.Mock.Called(params, uri) 32 | return args.Get(0).([]byte), args.Error(1) 33 | } 34 | 35 | func (client *MockClient) delete(uri string) error { 36 | args := client.Mock.Called(nil, uri) 37 | return args.Error(1) 38 | } 39 | -------------------------------------------------------------------------------- /optionals.go: -------------------------------------------------------------------------------- 1 | package twiliogo 2 | 3 | type Optional interface { 4 | GetParam() (string, string) 5 | } 6 | 7 | type Callback string 8 | 9 | func (callback Callback) GetParam() (string, string) { 10 | return "Url", string(callback) 11 | } 12 | 13 | type ApplicationSid string 14 | 15 | func (applicationSid ApplicationSid) GetParam() (string, string) { 16 | return "ApplicationSid", string(applicationSid) 17 | } 18 | 19 | type Method string 20 | 21 | func (method Method) GetParam() (string, string) { 22 | return "Method", string(method) 23 | } 24 | 25 | type FallbackUrl string 26 | 27 | func (fallbackUrl FallbackUrl) GetParam() (string, string) { 28 | return "FallbackUrl", string(fallbackUrl) 29 | } 30 | 31 | type FallbackMethod string 32 | 33 | func (fallbackMethod FallbackMethod) GetParam() (string, string) { 34 | return "FallbackMethod", string(fallbackMethod) 35 | } 36 | 37 | type StatusCallback string 38 | 39 | func (statusCallback StatusCallback) GetParam() (string, string) { 40 | return "StatusCallback", string(statusCallback) 41 | } 42 | 43 | type StatusCallbackMethod string 44 | 45 | func (statusCallbackMethod StatusCallbackMethod) GetParam() (string, string) { 46 | return "StatusCallbackMethod", string(statusCallbackMethod) 47 | } 48 | 49 | type SendDigits string 50 | 51 | func (sendDigits SendDigits) GetParam() (string, string) { 52 | return "SendDigits", string(sendDigits) 53 | } 54 | 55 | type IfMachine string 56 | 57 | func (ifMachine IfMachine) GetParam() (string, string) { 58 | return "IfMachine", string(ifMachine) 59 | } 60 | 61 | type Timeout string 62 | 63 | func (timeout Timeout) GetParam() (string, string) { 64 | return "Timeout", string(timeout) 65 | } 66 | 67 | type Record string 68 | 69 | func (record Record) GetParam() (string, string) { 70 | return "Record", string(record) 71 | } 72 | 73 | type To string 74 | 75 | func (to To) GetParam() (string, string) { 76 | return "To", string(to) 77 | } 78 | 79 | type From string 80 | 81 | func (from From) GetParam() (string, string) { 82 | return "From", string(from) 83 | } 84 | 85 | type Status string 86 | 87 | func (status Status) GetParam() (string, string) { 88 | return "Status", string(status) 89 | } 90 | 91 | type StartTime string 92 | 93 | func (startTime StartTime) GetParam() (string, string) { 94 | return "StartTime", string(startTime) 95 | } 96 | 97 | type ParentCallSid string 98 | 99 | func (parentCallSid ParentCallSid) GetParam() (string, string) { 100 | return "ParentCallSid", string(parentCallSid) 101 | } 102 | 103 | type DateSent string 104 | 105 | func (dateSent DateSent) GetParam() (string, string) { 106 | return "DateSent", string(dateSent) 107 | } 108 | 109 | type Body string 110 | 111 | func (body Body) GetParam() (string, string) { 112 | return "Body", string(body) 113 | } 114 | 115 | type MediaUrl string 116 | 117 | func (mediaUrl MediaUrl) GetParam() (string, string) { 118 | return "MediaUrl", string(mediaUrl) 119 | } 120 | 121 | type FriendlyName string 122 | 123 | func (friendlyName FriendlyName) GetParam() (string, string) { 124 | return "FriendlyName", string(friendlyName) 125 | } 126 | 127 | type PhoneNumber string 128 | 129 | func (phoneNumber PhoneNumber) GetParam() (string, string) { 130 | return "PhoneNumber", string(phoneNumber) 131 | } 132 | 133 | type AreaCode string 134 | 135 | func (areaCode AreaCode) GetParam() (string, string) { 136 | return "AreaCode", string(areaCode) 137 | } 138 | 139 | type MessagingServiceSid string 140 | 141 | func (sid MessagingServiceSid) GetParam() (string, string) { 142 | return "MessagingServiceSid", string(sid) 143 | } 144 | --------------------------------------------------------------------------------