-
Notifications
You must be signed in to change notification settings - Fork 26
/
sms.go
242 lines (210 loc) · 7.27 KB
/
sms.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package nexmo
import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
)
// SMS represents the SMS API functions for sending text messages.
type SMS struct {
client *Client
}
// SMS message types.
const (
Text = "text"
Binary = "binary"
WAPPush = "wappush"
Unicode = "unicode"
VCal = "vcal"
VCard = "vcard"
)
// MessageClass will be one of the following:
// - Flash
// - Standard
// - SIMData
// - Forward
type MessageClass int
// SMS message classes.
const (
// This type of SMS message is displayed on the mobile screen without being
// saved in the message store or on the SIM card; unless explicitly saved
// by the mobile user.
Flash MessageClass = iota
// This message is to be stored in the device memory or the SIM card
// (depending on memory availability).
Standard
// This message class carries SIM card data. The SIM card data must be
// successfully transferred prior to sending acknowledgment to the service
// center. An error message is sent to the service center if this
// transmission is not possible.
SIMData
// This message is forwarded from the receiving entity to an external
// device. The delivery acknowledgment is sent to the service center
// regardless of whether or not the message was forwarded to the external
// device.
Forward
)
var messageClassMap = map[MessageClass]string{
Flash: "flash",
Standard: "standard",
SIMData: "SIM data",
Forward: "forward",
}
func (m MessageClass) String() string {
return messageClassMap[m]
}
// MarshalJSON implements the json.Marshaller interface
func (m *SMSMessage) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
APIKey string `json:"api_key"`
APISecret string `json:"api_secret"`
SMSMessage
}{
APIKey: m.apiKey,
APISecret: m.apiSecret,
SMSMessage: *m,
})
}
// SMSMessage defines a single SMS message.
type SMSMessage struct {
apiKey string
apiSecret string
From string `json:"from"`
To string `json:"to"`
Type string `json:"type"`
Text string `json:"text,omitempty"` // Optional.
StatusReportRequired int `json:"status-report-req,omitempty"` // Optional.
ClientReference string `json:"client-ref,omitempty"` // Optional.
NetworkCode string `json:"network-code,omitempty"` // Optional.
VCard string `json:"vcrad,omitempty"` // Optional.
VCal string `json:"vcal,omitempty"` // Optional.
TTL int `json:"ttl,omitempty"` // Optional.
Class MessageClass `json:"message-class,omitempty"` // Optional.
Callback string `json:"callback,omitempty"` // Optional.
Body []byte `json:"body,omitempty"` // Required for Binary message.
UDH []byte `json:"udh,omitempty"` // Required for Binary message.
// The following is only for type=wappush
Title string `json:"title,omitempty"` // Title shown to recipient
URL string `json:"url,omitempty"` // WAP Push URL
Validity int `json:"validity,omitempty"` // Duration WAP Push is available in milliseconds
}
// A ResponseCode will be returned
// whenever an SMSMessage is sent.
type ResponseCode int
// String implements the fmt.Stringer interface
func (c ResponseCode) String() string {
return responseCodeMap[c]
}
// Possible response codes
const (
ResponseSuccess ResponseCode = iota
ResponseThrottled
ResponseMissingParams
ResponseInvalidParams
ResponseInvalidCredentials
ResponseInternalError
ResponseInvalidMessage
ResponseNumberBarred
ResponsePartnerAcctBarred
ResponsePartnerQuotaExceeded
ResponseUnused //This is not used yet.Left blank by Nexmo for the time being.
ResponseRESTNotEnabled
ResponseMessageTooLong
ResponseCommunicationFailed
ResponseInvalidSignature
ResponseInvalidSenderAddress
ResponseInvalidTTL
ResponseFacilityNotAllowed
ResponseInvalidMessageClass
)
var responseCodeMap = map[ResponseCode]string{
ResponseSuccess: "Success",
ResponseThrottled: "Throttled",
ResponseMissingParams: "Missing params",
ResponseInvalidParams: "Invalid params",
ResponseInvalidCredentials: "Invalid credentials",
ResponseInternalError: "Internal error",
ResponseInvalidMessage: "Invalid message",
ResponseNumberBarred: "Number barred",
ResponsePartnerAcctBarred: "Partner account barred",
ResponsePartnerQuotaExceeded: "Partner quota exceeded",
ResponseRESTNotEnabled: "Account not enabled for REST",
ResponseMessageTooLong: "Message too long",
ResponseCommunicationFailed: "Communication failed",
ResponseInvalidSignature: "Invalid signature",
ResponseInvalidSenderAddress: "Invalid sender address",
ResponseInvalidTTL: "Invalid TTL",
ResponseFacilityNotAllowed: "Facility not allowed",
ResponseInvalidMessageClass: "Invalid message class",
}
// MessageReport is the "status report" for a single SMS sent via the Nexmo API
type MessageReport struct {
Status ResponseCode `json:"status,string"`
MessageID string `json:"message-id"`
To string `json:"to"`
ClientReference string `json:"client-ref"`
RemainingBalance string `json:"remaining-balance"`
MessagePrice string `json:"message-price"`
Network string `json:"network"`
ErrorText string `json:"error-text"`
}
// MessageResponse contains the response from Nexmo's API after we attempt to
// send any kind of message.
// It will contain one MessageReport for every 160 chars sent.
type MessageResponse struct {
MessageCount int `json:"message-count,string"`
Messages []MessageReport `json:"messages"`
}
// Send the message using the specified SMS client.
func (c *SMS) Send(msg *SMSMessage) (*MessageResponse, error) {
if len(msg.From) <= 0 {
return nil, errors.New("Invalid From field specified")
}
if len(msg.To) <= 0 {
return nil, errors.New("Invalid To field specified")
}
if len(msg.ClientReference) > 40 {
return nil, errors.New("Client reference too long")
}
var messageResponse *MessageResponse
switch msg.Type {
case Text:
case Unicode:
if len(msg.Text) <= 0 {
return nil, errors.New("Invalid message text")
}
case Binary:
if len(msg.UDH) == 0 || len(msg.Body) == 0 {
return nil, errors.New("Invalid binary message")
}
case WAPPush:
if len(msg.URL) == 0 || len(msg.Title) == 0 {
return nil, errors.New("Invalid WAP Push parameters")
}
}
if !c.client.useOauth {
msg.apiKey = c.client.apiKey
msg.apiSecret = c.client.apiSecret
}
var r *http.Request
buf, err := json.Marshal(msg)
if err != nil {
return nil, errors.New("invalid message struct - unable to convert to JSON")
}
b := bytes.NewBuffer(buf)
r, _ = http.NewRequest("POST", apiRoot+"/sms/json", b)
r.Header.Add("Accept", "application/json")
r.Header.Add("Content-Type", "application/json")
resp, err := c.client.HTTPClient.Do(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
err = json.Unmarshal(body, &messageResponse)
if err != nil {
return nil, err
}
return messageResponse, nil
}