forked from nyaruka/courier
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mtarget.go
152 lines (130 loc) · 4.36 KB
/
mtarget.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
package mtarget
import (
"context"
"fmt"
"net/http"
"net/url"
"time"
"github.com/buger/jsonparser"
"github.com/nyaruka/courier"
"github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/utils"
"github.com/nyaruka/gocommon/urns"
)
var sendURL = "https://api-public.mtarget.fr/api-sms.json"
var maxLength = 765
var statuses = map[string]courier.MsgStatusValue{
"0": courier.MsgWired,
"1": courier.MsgWired,
"2": courier.MsgSent,
"3": courier.MsgDelivered,
"4": courier.MsgFailed,
"6": courier.MsgFailed,
}
func init() {
courier.RegisterHandler(newHandler())
}
type handler struct {
handlers.BaseHandler
}
func newHandler() courier.ChannelHandler {
return &handler{handlers.NewBaseHandler(courier.ChannelType("MT"), "Mtarget")}
}
// Initialize is called by the engine once everything is loaded
func (h *handler) Initialize(s courier.Server) error {
h.SetServer(s)
err := s.AddHandlerRoute(h, http.MethodPost, "receive", h.receiveMsg)
if err != nil {
return nil
}
statusHandler := handlers.NewExternalIDStatusHandler(h.BaseHandler, statuses, "MsgId", "Status")
return s.AddHandlerRoute(h, http.MethodPost, "status", statusHandler)
}
// ReceiveMsg handles both MO messages and Stop commands
func (h *handler) receiveMsg(ctx context.Context, c courier.Channel, w http.ResponseWriter, r *http.Request) ([]courier.Event, error) {
err := r.ParseForm()
if err != nil {
return nil, courier.WriteAndLogRequestError(ctx, w, r, c, err)
}
text := r.Form.Get("Content")
from := r.Form.Get("Msisdn")
keyword := r.Form.Get("Keyword")
if from == "" {
return nil, courier.WriteAndLogRequestError(ctx, w, r, c, fmt.Errorf("missing required field 'Msisdn'"))
}
// create our URN
urn := urns.NewTelURNForCountry(from, c.Country())
// if this a stop command, shortcut stopping that contact
if keyword == "Stop" {
stop := h.Backend().NewChannelEvent(c, courier.StopContact, urn)
err := h.Backend().WriteChannelEvent(ctx, stop)
if err != nil {
return nil, err
}
return []courier.Event{stop}, courier.WriteChannelEventSuccess(ctx, w, r, stop)
}
// otherwise, create our incoming message and write that
msg := h.Backend().NewIncomingMsg(c, urn, text).WithReceivedOn(time.Now().UTC())
err = h.Backend().WriteMsg(ctx, msg)
if err != nil {
return nil, err
}
return []courier.Event{msg}, courier.WriteMsgSuccess(ctx, w, r, []courier.Msg{msg})
}
// SendMsg sends the passed in message, returning any error
func (h *handler) SendMsg(ctx context.Context, msg courier.Msg) (courier.MsgStatus, error) {
username := msg.Channel().StringConfigForKey(courier.ConfigUsername, "")
if username == "" {
return nil, fmt.Errorf("no username set for MT channel")
}
password := msg.Channel().StringConfigForKey(courier.ConfigPassword, "")
if password == "" {
return nil, fmt.Errorf("no password set for MT channel")
}
// send our message
status := h.Backend().NewMsgStatusForID(msg.Channel(), msg.ID(), courier.MsgErrored)
for _, part := range handlers.SplitMsg(handlers.GetTextAndAttachments(msg), maxLength) {
// build our request
params := url.Values{
"username": []string{username},
"password": []string{password},
"msisdn": []string{msg.URN().Path()},
"msg": []string{part},
"serviceid": []string{msg.Channel().Address()},
}
msgURL, _ := url.Parse(sendURL)
msgURL.RawQuery = params.Encode()
req, err := http.NewRequest(http.MethodPost, msgURL.String(), nil)
if err != nil {
return nil, err
}
rr, err := utils.MakeHTTPRequest(req)
log := courier.NewChannelLogFromRR("Message Sent", msg.Channel(), msg.ID(), rr).WithError("Message Send Error", err)
status.AddLog(log)
if err != nil {
break
}
// parse our response for our status code and ticket (external id)
// {
// "results": [{
// "msisdn": "+447xxxxxxxx",
// "smscount": "1",
// "code": "0",
// "reason": "ACCEPTED",
// "ticket": "760eeaa0-5034-11e7-bb92-00000a0a643a"
// }]
// }
code, _ := jsonparser.GetString(rr.Body, "results", "[0]", "code")
externalID, _ := jsonparser.GetString(rr.Body, "results", "[0]", "ticket")
if code == "0" && externalID != "" {
// all went well, set ourselves to wired
status.SetStatus(courier.MsgWired)
status.SetExternalID(externalID)
} else {
status.SetStatus(courier.MsgFailed)
log.WithError("Message Send Error", fmt.Errorf("Error status code, failing permanently"))
break
}
}
return status, nil
}