short_message 1.1.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -0
- data/README.md +370 -24
- data/Rakefile +2 -3
- data/app/controllers/short_message/messages_controller.rb +35 -20
- data/app/mailers/short_message/application_mailer.rb +6 -0
- data/app/mailers/short_message/mailer.rb +17 -3
- data/app/models/short_message/application_record.rb +5 -0
- data/app/models/short_message/message.rb +154 -44
- data/config/routes.rb +1 -2
- data/db/migrate/20130308133457_create_short_message_messages.rb +1 -1
- data/db/migrate/20260608090000_add_delivery_metadata_to_short_message_messages.rb +14 -0
- data/lib/generators/short_message/templates/config/initializers/short_message.rb +16 -15
- data/lib/short_message/config.rb +14 -28
- data/lib/short_message/engine.rb +0 -4
- data/lib/short_message/version.rb +1 -1
- metadata +44 -91
- data/test/dummy/README.rdoc +0 -28
- data/test/dummy/Rakefile +0 -6
- data/test/dummy/app/assets/javascripts/application.js +0 -13
- data/test/dummy/app/assets/stylesheets/application.css +0 -15
- data/test/dummy/app/controllers/application_controller.rb +0 -5
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/views/layouts/application.html.erb +0 -13
- data/test/dummy/bin/bundle +0 -3
- data/test/dummy/bin/rails +0 -4
- data/test/dummy/bin/rake +0 -4
- data/test/dummy/bin/setup +0 -29
- data/test/dummy/config/application.rb +0 -26
- data/test/dummy/config/boot.rb +0 -5
- data/test/dummy/config/database.yml +0 -54
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -41
- data/test/dummy/config/environments/production.rb +0 -79
- data/test/dummy/config/environments/test.rb +0 -42
- data/test/dummy/config/initializers/assets.rb +0 -11
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
- data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
- data/test/dummy/config/initializers/inflections.rb +0 -16
- data/test/dummy/config/initializers/mime_types.rb +0 -4
- data/test/dummy/config/initializers/session_store.rb +0 -3
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -23
- data/test/dummy/config/routes.rb +0 -4
- data/test/dummy/config/secrets.yml +0 -22
- data/test/dummy/config.ru +0 -4
- data/test/dummy/public/404.html +0 -67
- data/test/dummy/public/422.html +0 -67
- data/test/dummy/public/500.html +0 -66
- data/test/dummy/public/favicon.ico +0 -0
- data/test/integration/navigation_test.rb +0 -8
- data/test/short_message_test.rb +0 -7
- data/test/test_helper.rb +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 84df6eda6507a99c19b2389c62b3d1bfd1f7624a87aed826533c6162ced857a3
|
|
4
|
+
data.tar.gz: 18a283ab3dd2cf9a49eb8170c38d51f0a8dafd4b2105d43f088b4a3aeffd7f60
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a0f5a7a88e8480910af30ff20be783fbbf8abc73fa41dee4dd8a29ddb4346040588d7effd776ea3e699f6ee4ba2be82bccd0b8245a5454a35e22d38c13248834
|
|
7
|
+
data.tar.gz: 321c0fc81da7c2d44b727a9481442ab89ff10ecd3f75b92e120fa1894efe626edf95529e97889c16efc28c2cea9a11b95de9a752db3179293148bf8e37773e45
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.0.0 - 2026-06-09
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
- Removed legacy GTX v1 user/pass delivery mode.
|
|
7
|
+
- Removed legacy configuration keys: gateway_server, gateway_port, send_file_path, account_functions_path, user, password.
|
|
8
|
+
- Delivery now requires api_key and uses GTX REST API v2 endpoint semantics.
|
|
9
|
+
- Removed legacy customization hook based on build_deliver_params_string.
|
|
10
|
+
- Updated runtime requirements to Ruby 3.2+ and Rails 6.1+.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Delivery metadata persistence fields and indexes:
|
|
14
|
+
- provider_http_status
|
|
15
|
+
- provider_status
|
|
16
|
+
- provider_response_body
|
|
17
|
+
- unique index on message_key
|
|
18
|
+
- Optional callback token validation for status endpoint.
|
|
19
|
+
- Tests for delivery success/failure and callback authorization.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Improved HTTP/TLS safety defaults and timeout configuration.
|
|
23
|
+
- Development/test DB defaults now use sqlite3 in dummy app.
|
|
24
|
+
- Updated Rails compatibility to support Rails 8 (gemspec constraint raised to `< 9.0`).
|
|
25
|
+
- `error_notification` mailer now renders a clean error summary instead of serializing the raw HTTP response object.
|
|
26
|
+
- `payment_required_notification` mailer now references `api_base_url` instead of the removed `gateway_server` config key.
|
|
27
|
+
|
|
28
|
+
### Removed
|
|
29
|
+
- Dropped all Rails 4/5 compatibility shims: `skip_filter`, `render text:`, and the `deliver_method` version guard.
|
|
30
|
+
- Removed `require_dependency` usage (removed in Rails 7).
|
|
31
|
+
- Removed unused `param_name` metaprogramming from `Configuration`.
|
|
32
|
+
- Raised initial migration version from `[4.2]` to `[6.1]`.
|
data/README.md
CHANGED
|
@@ -2,74 +2,420 @@
|
|
|
2
2
|
|
|
3
3
|
[](http://badge.fury.io/rb/short_message)
|
|
4
4
|
|
|
5
|
+
ShortMessage is a Rails Engine that sends SMS messages via the GTX Messaging REST API v2 and receives delivery status callbacks.
|
|
6
|
+
|
|
7
|
+
**Requires:** Ruby >= 3.2, Rails >= 6.1
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [ShortMessage](#shortmessage)
|
|
14
|
+
- [Table of Contents](#table-of-contents)
|
|
15
|
+
- [Installation](#installation)
|
|
16
|
+
- [Configuration](#configuration)
|
|
17
|
+
- [Configuration reference](#configuration-reference)
|
|
18
|
+
- [Usage](#usage)
|
|
19
|
+
- [Message attributes](#message-attributes)
|
|
20
|
+
- [Delivery Reports](#delivery-reports)
|
|
21
|
+
- [Events](#events)
|
|
22
|
+
- [`short_message.delivered`](#short_messagedelivered)
|
|
23
|
+
- [`short_message.status_updated`](#short_messagestatus_updated)
|
|
24
|
+
- [Email Notifications](#email-notifications)
|
|
25
|
+
- [Customization](#customization)
|
|
26
|
+
- [Status code labels](#status-code-labels)
|
|
27
|
+
- [Extending the request payload](#extending-the-request-payload)
|
|
28
|
+
- [Development](#development)
|
|
29
|
+
- [Testing DLR callbacks locally](#testing-dlr-callbacks-locally)
|
|
30
|
+
- [Building \& Releasing](#building--releasing)
|
|
31
|
+
- [Local Development (using in another project)](#local-development-using-in-another-project)
|
|
32
|
+
- [Upgrade Guide (1.x → 2.0)](#upgrade-guide-1x--20)
|
|
33
|
+
- [License](#license)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
5
37
|
## Installation
|
|
6
38
|
|
|
7
|
-
Add
|
|
39
|
+
Add to your Gemfile:
|
|
8
40
|
|
|
9
41
|
```ruby
|
|
10
42
|
gem 'short_message'
|
|
11
43
|
```
|
|
12
44
|
|
|
13
|
-
|
|
45
|
+
Install dependencies:
|
|
14
46
|
|
|
15
47
|
```console
|
|
16
48
|
bundle install
|
|
17
49
|
```
|
|
18
50
|
|
|
19
|
-
Run the generator:
|
|
51
|
+
Run the install generator:
|
|
20
52
|
|
|
21
53
|
```console
|
|
22
54
|
rails generate short_message:install
|
|
23
55
|
```
|
|
24
56
|
|
|
25
|
-
|
|
57
|
+
Copy and run the migrations:
|
|
26
58
|
|
|
27
59
|
```console
|
|
28
|
-
|
|
60
|
+
bundle exec rails railties:install:migrations FROM=short_message
|
|
61
|
+
bundle exec rails db:migrate
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
|
|
68
|
+
The generator creates `config/initializers/short_message.rb`. Edit it to suit your setup:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
ShortMessage.configure do |config|
|
|
72
|
+
# --- Required ---
|
|
73
|
+
config.api_base_url = "https://rest.gtx-messaging.net"
|
|
74
|
+
config.api_key = ENV.fetch("GTX_API_KEY")
|
|
75
|
+
config.response_format = "json" # "json", "xml", or "plain"
|
|
76
|
+
|
|
77
|
+
# --- SMS defaults ---
|
|
78
|
+
config.default_sms_sender = "MyApp" # sender name or number used when none is set on the message
|
|
79
|
+
|
|
80
|
+
# --- Delivery reports (DLR) ---
|
|
81
|
+
config.dlr_mask = 3 # 1 = delivered, 2 = undelivered, 3 = both
|
|
82
|
+
config.callback_token = ENV["SHORT_MESSAGE_CALLBACK_TOKEN"]
|
|
83
|
+
config.dlr_url = "https://example.com/short_message/messages/status?token=#{config.callback_token}"
|
|
84
|
+
|
|
85
|
+
# --- HTTP timeouts ---
|
|
86
|
+
config.open_timeout = 5 # seconds
|
|
87
|
+
config.read_timeout = 30 # seconds
|
|
88
|
+
|
|
89
|
+
# --- Email notifications (optional) ---
|
|
90
|
+
config.default_mail_sender = "sms@example.com"
|
|
91
|
+
config.admin_notification_email = "admin@example.com" # set nil to disable error emails
|
|
92
|
+
config.reload_notification_email = "billing@example.com" # notified on HTTP 402 from GTX
|
|
93
|
+
end
|
|
29
94
|
```
|
|
30
95
|
|
|
96
|
+
### Configuration reference
|
|
97
|
+
|
|
98
|
+
| Key | Required | Default | Description |
|
|
99
|
+
| --------------------------- | -------- | ---------------------------------- | -------------------------------------------------- |
|
|
100
|
+
| `api_base_url` | yes | `"https://rest.gtx-messaging.net"` | GTX API endpoint |
|
|
101
|
+
| `api_key` | yes | — | GTX API authentication key |
|
|
102
|
+
| `response_format` | no | `"json"` | `"json"`, `"xml"`, or `"plain"` |
|
|
103
|
+
| `default_sms_sender` | no | — | Fallback sender when none is set on the message |
|
|
104
|
+
| `dlr_mask` | no | `3` | Delivery report event mask |
|
|
105
|
+
| `dlr_url` | no | — | Callback URL GTX posts status updates to |
|
|
106
|
+
| `callback_token` | no | — | Token validated on incoming status callbacks |
|
|
107
|
+
| `open_timeout` | no | `5` | HTTP connection open timeout (seconds) |
|
|
108
|
+
| `read_timeout` | no | `30` | HTTP read timeout (seconds) |
|
|
109
|
+
| `default_mail_sender` | no | — | From address for notification emails |
|
|
110
|
+
| `admin_notification_email` | no | — | Receives error notifications; set `nil` to disable |
|
|
111
|
+
| `reload_notification_email` | no | — | Receives payment-required (HTTP 402) alerts |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
31
115
|
## Usage
|
|
32
116
|
|
|
33
|
-
Create a
|
|
117
|
+
Create a `ShortMessage::Message` and call `deliver`:
|
|
34
118
|
|
|
35
119
|
```ruby
|
|
36
|
-
|
|
37
|
-
|
|
120
|
+
sms = ShortMessage::Message.new(
|
|
121
|
+
sender: "+41791234567", # or a name up to 11 chars
|
|
122
|
+
recipient: "+41799876543",
|
|
123
|
+
text: "Hello World!"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
sms.deliver # => true on success, false on failure
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`sender` is optional when `config.default_sms_sender` is configured.
|
|
130
|
+
|
|
131
|
+
Recipient normalization: if `recipient` starts with `00`, the gem automatically converts it to `+` format (for example `0041791234567` -> `+41791234567`). Any other non-E.164 recipient format is rejected before sending.
|
|
132
|
+
|
|
133
|
+
After a successful delivery, `sms.message_key` holds the provider-assigned message ID and `sms.provider_http_status` holds the HTTP status code returned by GTX.
|
|
134
|
+
|
|
135
|
+
### Message attributes
|
|
136
|
+
|
|
137
|
+
| Attribute | Description |
|
|
138
|
+
| ------------------------ | ----------------------------------------------- |
|
|
139
|
+
| `sender` | SMS sender name or number |
|
|
140
|
+
| `recipient` | SMS recipient phone number in E.164 (`+...`) |
|
|
141
|
+
| `text` | Message body |
|
|
142
|
+
| `message_key` | Unique ID assigned by GTX after delivery |
|
|
143
|
+
| `status_code` | Delivery status code (updated via DLR callback) |
|
|
144
|
+
| `provider_http_status` | HTTP status from GTX at delivery time |
|
|
145
|
+
| `provider_status` | Status string from GTX response |
|
|
146
|
+
| `provider_response_body` | Full raw response payload from GTX |
|
|
147
|
+
| `error_code` | Error code if delivery failed |
|
|
148
|
+
| `error_message` | Error description if delivery failed |
|
|
149
|
+
| `submitted_at` | Timestamp when message was submitted to GTX |
|
|
150
|
+
| `delivered_at` | Timestamp when delivery confirmed |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Delivery Reports
|
|
155
|
+
|
|
156
|
+
ShortMessage mounts a status-update endpoint at `/short_message/messages/status`. Configure GTX to POST (or GET) to this URL with the message ID and status:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
POST /short_message/messages/status?id=<message_key>&status=<code>&token=<callback_token>
|
|
38
160
|
```
|
|
39
161
|
|
|
40
|
-
|
|
162
|
+
| Parameter | Description |
|
|
163
|
+
| --------- | -------------------------------------------- |
|
|
164
|
+
| `id` | The `message_key` of the message to update |
|
|
165
|
+
| `status` | Numeric status code from GTX |
|
|
166
|
+
| `token` | Required when `config.callback_token` is set |
|
|
167
|
+
|
|
168
|
+
**Response codes**
|
|
169
|
+
|
|
170
|
+
| HTTP | Meaning |
|
|
171
|
+
| ---- | ---------------------------------- |
|
|
172
|
+
| 200 | Status updated successfully |
|
|
173
|
+
| 400 | Missing `id` or `status` parameter |
|
|
174
|
+
| 401 | Invalid or missing callback token |
|
|
175
|
+
| 404 | Message not found |
|
|
176
|
+
|
|
177
|
+
When `config.callback_token` is set, every incoming callback must include `token=<value>` or it will be rejected with a 401.
|
|
178
|
+
|
|
179
|
+
---
|
|
41
180
|
|
|
42
|
-
|
|
181
|
+
## Events
|
|
182
|
+
|
|
183
|
+
ShortMessage fires `ActiveSupport::Notifications` events so you can react to delivery and status changes without monkey-patching.
|
|
184
|
+
|
|
185
|
+
### `short_message.delivered`
|
|
186
|
+
|
|
187
|
+
Fired after a message is successfully sent to GTX.
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
ActiveSupport::Notifications.subscribe('short_message.delivered') do |name, start, finish, id, payload|
|
|
191
|
+
Rails.logger.info "SMS #{payload[:key]} delivered"
|
|
192
|
+
end
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Payload keys: `key` (the `message_key` assigned by GTX).
|
|
196
|
+
|
|
197
|
+
### `short_message.status_updated`
|
|
198
|
+
|
|
199
|
+
Fired when a delivery report callback is received.
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
ActiveSupport::Notifications.subscribe('short_message.status_updated') do |name, start, finish, id, payload|
|
|
203
|
+
Activity.create(
|
|
204
|
+
message: "Message #{payload[:key]} status → #{payload[:status]}"
|
|
205
|
+
)
|
|
206
|
+
end
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Payload keys: `key` (message ID), `status` (new status code).
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Email Notifications
|
|
214
|
+
|
|
215
|
+
When `admin_notification_email` is configured, ShortMessage sends an email on delivery failure or unexpected exceptions. Set it to `nil` to disable.
|
|
216
|
+
|
|
217
|
+
When `reload_notification_email` is configured, ShortMessage sends an email when GTX returns HTTP 402 (payment required / credit exhausted).
|
|
218
|
+
|
|
219
|
+
---
|
|
43
220
|
|
|
44
221
|
## Customization
|
|
45
222
|
|
|
46
|
-
### Status
|
|
223
|
+
### Status code labels
|
|
47
224
|
|
|
48
|
-
|
|
225
|
+
Status code descriptions are stored in locale files under `config/locales/`. Add or override keys there to customise the text returned by `message.status_text`.
|
|
49
226
|
|
|
50
|
-
|
|
227
|
+
```yaml
|
|
228
|
+
# config/locales/short_message.en.yml
|
|
229
|
+
en:
|
|
230
|
+
short_message:
|
|
231
|
+
status:
|
|
232
|
+
code_1: "Delivered to handset"
|
|
233
|
+
code_2: "Delivery failed"
|
|
234
|
+
code_4: "Queued on SMSC"
|
|
235
|
+
code_8: "Delivered to SMSC"
|
|
236
|
+
code_16: "Not delivered to SMSC"
|
|
237
|
+
```
|
|
51
238
|
|
|
52
|
-
|
|
239
|
+
```ruby
|
|
240
|
+
sms.status_text # => "Delivered to handset"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Extending the request payload
|
|
244
|
+
|
|
245
|
+
Override `build_deliver_params_hash` in an initializer to add or change parameters sent to GTX:
|
|
53
246
|
|
|
54
247
|
```ruby
|
|
55
248
|
ShortMessage::Message.module_eval do
|
|
56
249
|
private
|
|
57
|
-
def build_deliver_params_string
|
|
58
|
-
# your code here
|
|
59
|
-
end
|
|
60
250
|
|
|
61
|
-
def
|
|
62
|
-
#
|
|
251
|
+
def build_deliver_params_hash
|
|
252
|
+
super.merge("coding" => 2) # e.g. enable UCS-2 encoding
|
|
63
253
|
end
|
|
64
254
|
end
|
|
65
255
|
```
|
|
66
256
|
|
|
67
|
-
|
|
257
|
+
---
|
|
68
258
|
|
|
69
|
-
|
|
259
|
+
## Development
|
|
260
|
+
|
|
261
|
+
Clone the repo and install dependencies:
|
|
262
|
+
|
|
263
|
+
```console
|
|
264
|
+
git clone https://github.com/asaurer/short_message.git
|
|
265
|
+
cd short_message
|
|
266
|
+
bundle install
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Run the test suite (migrations run automatically):
|
|
270
|
+
|
|
271
|
+
```console
|
|
272
|
+
bundle exec rake test
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Testing DLR callbacks locally
|
|
276
|
+
|
|
277
|
+
GTX needs a publicly reachable HTTPS URL to deliver status callbacks. Use [ngrok](https://ngrok.com) to expose your local server:
|
|
278
|
+
|
|
279
|
+
```console
|
|
280
|
+
ngrok http 3000
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Then configure the tunnel URL in your initializer:
|
|
70
284
|
|
|
71
285
|
```ruby
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
286
|
+
config.callback_token = "dev-secret"
|
|
287
|
+
config.dlr_url = "https://abc123.ngrok.io/short_message/messages/status?token=dev-secret"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Restart your Rails server after changing the initializer. Once a message is delivered, GTX will POST the status update to the ngrok URL which forwards it to your local app.
|
|
291
|
+
|
|
292
|
+
You can also simulate a callback manually without ngrok:
|
|
293
|
+
|
|
294
|
+
```console
|
|
295
|
+
curl -X POST "http://localhost:3000/short_message/messages/status" \
|
|
296
|
+
-d "id=YOUR_MESSAGE_KEY&status=1&token=dev-secret"
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Replace `YOUR_MESSAGE_KEY` with the value of `sms.message_key` after calling `sms.deliver`.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Building & Releasing
|
|
304
|
+
|
|
305
|
+
Build the gem locally:
|
|
306
|
+
|
|
307
|
+
```console
|
|
308
|
+
gem build short_message.gemspec
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
This creates `short_message-<version>.gem` in the project root.
|
|
312
|
+
|
|
313
|
+
Install the built gem locally to verify it works:
|
|
314
|
+
|
|
315
|
+
```console
|
|
316
|
+
gem install short_message-<version>.gem
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Push to RubyGems:
|
|
320
|
+
|
|
321
|
+
```console
|
|
322
|
+
gem push short_message-<version>.gem
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
You need a [RubyGems account](https://rubygems.org) with push access to the `short_message` gem. Credentials are stored in `~/.gem/credentials`.
|
|
326
|
+
|
|
327
|
+
**Release checklist:**
|
|
328
|
+
|
|
329
|
+
1. Update `lib/short_message/version.rb` with the new version number
|
|
330
|
+
2. Update `CHANGELOG.md` with the release notes
|
|
331
|
+
3. Commit the version bump and changelog
|
|
332
|
+
4. Build and push the gem
|
|
333
|
+
5. Tag the release: `git tag v<version> && git push origin v<version>`
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Local Development (using in another project)
|
|
338
|
+
|
|
339
|
+
When you want to work on `short_message` and test changes inside a host Rails app simultaneously, point the host app's Gemfile at your local checkout instead of RubyGems:
|
|
340
|
+
|
|
341
|
+
```ruby
|
|
342
|
+
# host-app/Gemfile
|
|
343
|
+
gem 'short_message', path: '/path/to/short_message'
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Then in the host app:
|
|
347
|
+
|
|
348
|
+
```console
|
|
349
|
+
bundle install
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Bundler symlinks the gem from the local path, so every change you make in the `short_message` directory is picked up immediately on the next request (or `reload!` in the console) without re-running `bundle install`.
|
|
353
|
+
|
|
354
|
+
If the gem has pending migrations that need to land in the host app's database:
|
|
355
|
+
|
|
356
|
+
```console
|
|
357
|
+
bundle exec rails railties:install:migrations FROM=short_message
|
|
358
|
+
bundle exec rails db:migrate
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
To re-generate the initializer or locales after making changes to the generator templates:
|
|
362
|
+
|
|
363
|
+
```console
|
|
364
|
+
bundle exec rails generate short_message:install
|
|
75
365
|
```
|
|
366
|
+
|
|
367
|
+
When you are done and want to switch back to the published gem, restore the Gemfile entry:
|
|
368
|
+
|
|
369
|
+
```ruby
|
|
370
|
+
gem 'short_message', '~> 2.0'
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Upgrade Guide (1.x → 2.0)
|
|
376
|
+
|
|
377
|
+
1. Replace old GTX config keys with `api_base_url`, `api_key`, and `response_format`.
|
|
378
|
+
2. Remove any initializer override of `build_deliver_params_string` (renamed to `build_deliver_params_hash`).
|
|
379
|
+
3. Install and run the new migration:
|
|
380
|
+
|
|
381
|
+
```console
|
|
382
|
+
bundle exec rails railties:install:migrations FROM=short_message
|
|
383
|
+
bundle exec rails db:migrate
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
If the `short_message_messages` table already
|
|
387
|
+
exists you will see `Mysql2::Error: Table 'short_message_messages' already exists`.
|
|
388
|
+
This happens because `railties:install:migrations` re-copies the original
|
|
389
|
+
create migration under a new timestamp. Mark it as already applied, then migrate:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
bundle exec rails runner \
|
|
393
|
+
"ActiveRecord::Base.connection.execute(
|
|
394
|
+
\"INSERT IGNORE INTO schema_migrations (version) \" \
|
|
395
|
+
\"VALUES ('$(ls db/migrate/*create_short_message_messages.short_message.rb \
|
|
396
|
+
| grep -oP '[0-9]+(?=_)')' )\")"
|
|
397
|
+
bundle exec rails db:migrate
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
SQLite users: replace `INSERT IGNORE` with `INSERT OR IGNORE`.
|
|
401
|
+
|
|
402
|
+
4. Optionally refresh the initializer and locales:
|
|
403
|
+
|
|
404
|
+
```console
|
|
405
|
+
bundle exec rails generate short_message:install
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
5. Configure delivery reports — set `config.dlr_url` to your callback endpoint and `config.callback_token` for request validation:
|
|
409
|
+
|
|
410
|
+
```ruby
|
|
411
|
+
config.callback_token = ENV["SHORT_MESSAGE_CALLBACK_TOKEN"]
|
|
412
|
+
config.dlr_url = "https://your-app.example/short_message/messages/status?token=#{config.callback_token}"
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
In the GTX portal, ensure delivery report callbacks are enabled for your API key and that your app's callback URL is publicly reachable over HTTPS.
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## License
|
|
420
|
+
|
|
421
|
+
MIT — see [MIT-LICENSE](MIT-LICENSE).
|
data/Rakefile
CHANGED
|
@@ -18,9 +18,6 @@ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
|
|
18
18
|
load 'rails/tasks/engine.rake'
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
load 'rails/tasks/statistics.rake'
|
|
22
|
-
|
|
23
|
-
|
|
24
21
|
|
|
25
22
|
Bundler::GemHelper.install_tasks
|
|
26
23
|
|
|
@@ -33,5 +30,7 @@ Rake::TestTask.new(:test) do |t|
|
|
|
33
30
|
t.verbose = false
|
|
34
31
|
end
|
|
35
32
|
|
|
33
|
+
Rake::Task[:test].enhance(["app:db:migrate"])
|
|
34
|
+
|
|
36
35
|
|
|
37
36
|
task default: :test
|
|
@@ -1,29 +1,44 @@
|
|
|
1
|
-
require_dependency "short_message/application_controller"
|
|
2
|
-
|
|
3
1
|
module ShortMessage
|
|
4
2
|
class MessagesController < ApplicationController
|
|
3
|
+
skip_before_action :verify_authenticity_token, only: :status
|
|
4
|
+
|
|
5
5
|
def status
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
message.status_code = params[:status]
|
|
9
|
-
message.save!
|
|
10
|
-
|
|
11
|
-
ActiveSupport::Notifications.instrument('short_message.status_updated', options: { key: params[:id], status: params[:status] })
|
|
12
|
-
message = "Message #{params[:id]} has now status #{params[:status]}"
|
|
13
|
-
else
|
|
14
|
-
message = "Message #{params[:id]} not found!"
|
|
15
|
-
status = 404
|
|
16
|
-
end
|
|
17
|
-
else
|
|
18
|
-
message = "Message ID or status not provided!"
|
|
19
|
-
status = 400
|
|
6
|
+
if callback_token_invalid?
|
|
7
|
+
return render plain: "Unauthorized", status: :unauthorized
|
|
20
8
|
end
|
|
21
9
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
message_id = params[:id] || params[:"message-id"]
|
|
11
|
+
dlr_mask = params[:"dlr-mask"]
|
|
12
|
+
error_code = params[:"error-code"]
|
|
13
|
+
|
|
14
|
+
return render plain: "OK", status: :ok if message_id.blank?
|
|
15
|
+
|
|
16
|
+
record = ShortMessage::Message.find_by(message_key: message_id)
|
|
17
|
+
return render plain: "OK", status: :ok unless record
|
|
18
|
+
|
|
19
|
+
update_params = {}
|
|
20
|
+
update_params[:status_code] = dlr_mask.to_i if dlr_mask.present?
|
|
21
|
+
|
|
22
|
+
# Only store error fields if error-code is not "0000" (success)
|
|
23
|
+
if error_code.present? && error_code != "0000"
|
|
24
|
+
update_params[:error_code] = error_code
|
|
25
|
+
update_params[:error_message] = params[:"error-message"] if params[:"error-message"].present?
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
update_params[:submitted_at] = params[:"submit-date"] if params[:"submit-date"].present?
|
|
29
|
+
update_params[:delivered_at] = params[:"done-date"] if params[:"done-date"].present?
|
|
30
|
+
|
|
31
|
+
record.update(update_params)
|
|
32
|
+
render plain: "OK", status: :ok
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def callback_token_invalid?
|
|
38
|
+
expected_token = ShortMessage.config.callback_token.to_s
|
|
39
|
+
return false if expected_token.empty?
|
|
40
|
+
|
|
41
|
+
params[:token].to_s != expected_token
|
|
27
42
|
end
|
|
28
43
|
end
|
|
29
44
|
end
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
module ShortMessage
|
|
2
2
|
class Mailer < ApplicationMailer
|
|
3
|
-
def error_notification
|
|
4
|
-
|
|
3
|
+
def error_notification(message, response_or_error)
|
|
4
|
+
details = if response_or_error.respond_to?(:code)
|
|
5
|
+
"HTTP #{response_or_error.code}: #{response_or_error.body.to_s.truncate(500)}"
|
|
6
|
+
else
|
|
7
|
+
"#{response_or_error.class}: #{response_or_error.message}"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
mail(
|
|
11
|
+
to: ShortMessage.config.admin_notification_email,
|
|
12
|
+
subject: "Error delivering SMS to #{message.recipient}",
|
|
13
|
+
body: "SMS from #{message.sender} to #{message.recipient} could not be sent!\r\n\r\nResponse: #{details}"
|
|
14
|
+
)
|
|
5
15
|
end
|
|
6
16
|
|
|
7
17
|
def payment_required_notification
|
|
8
|
-
mail
|
|
18
|
+
mail(
|
|
19
|
+
to: ShortMessage.config.reload_notification_email,
|
|
20
|
+
subject: "SMS Gateway requires payment",
|
|
21
|
+
body: "Your SMS Gateway API (#{ShortMessage.config.api_base_url}) requires payment!"
|
|
22
|
+
)
|
|
9
23
|
end
|
|
10
24
|
end
|
|
11
25
|
end
|