mitake_sms 2.0.0 → 3.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 +53 -1
- data/README.md +95 -109
- data/lib/mitake_sms/client.rb +79 -193
- data/lib/mitake_sms/response.rb +106 -15
- data/lib/mitake_sms/version.rb +1 -1
- data/lib/mitake_sms.rb +7 -42
- metadata +11 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0cf544b42ee5585ab48ee884c607ac8172d612050808ad6d7a04d3d1b50f84de
|
|
4
|
+
data.tar.gz: 5ab5cb554757c3f064c130941bc5f6c7e1fb46b861d80f07031dbdc466eccf19
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 528e04551ed3d097c43c513ea289096167b58c16b0a11637fc6ba0baa414a1082cc0ffbbac11ea470349aab2f536fe4280736340929d5eb468bbe7680ff72b29
|
|
7
|
+
data.tar.gz: f40efa10e466af71bea9cabb2d5f18dbf82a38250528f7cf319d7aac19ab02141fd0759105890f08c82f7e03e44f8881a801382a6cffbaca90bc747a35cdcdd6
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,59 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [3.0.0] - 2026-07-28
|
|
9
|
+
### Changed
|
|
10
|
+
- Raised `required_ruby_version` to `>= 3.3.0`, from `>= 2.6.0`. CI now runs against
|
|
11
|
+
3.3, 3.4 and ruby/head rather than 3.3 alone
|
|
12
|
+
- Raised the Faraday floor to `>= 2.14.3`, which is the oldest release without
|
|
13
|
+
CVE-2026-54297 (`NestedParamsEncoder` recursion DoS), CVE-2026-33637 and
|
|
14
|
+
CVE-2026-25765 (SSRF via protocol-relative URL host override). This drops support
|
|
15
|
+
for the Faraday 1.x line, whose patched `1.10.6` cannot be expressed alongside the
|
|
16
|
+
2.x floor in a single requirement
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `Response#message_ids`, `#client_id`, `#duplicate?`, `#sms_point` and `#records`, exposing the
|
|
20
|
+
`[clientid]`, `Duplicate` and `smsPoint` fields the API returns but the parser previously dropped
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- `batch_send` raises `ArgumentError` when a field other than `:text` contains `$$` or a line break.
|
|
24
|
+
Both are `SmBulkSend` delimiters, so a `destname` holding either one silently shifted the remaining
|
|
25
|
+
fields or split the row in two, corrupting that message and every field after it
|
|
26
|
+
- `batch_send` forwards options such as `:objectID` and `:smsPointFlag` to the API. It previously
|
|
27
|
+
accepted and discarded everything except `:charset`, so `smsPoint` could never be requested
|
|
28
|
+
- Message bodies containing `\r\n` or a bare `\r` are now converted to ASCII code 6 like `\n` already
|
|
29
|
+
was. A `\r\n` body previously kept a stray carriage return, and a `\r`-only body was left unconverted
|
|
30
|
+
- Message bodies are transcoded to UTF-8 before sending, so a Big5 or otherwise non-UTF-8 string
|
|
31
|
+
arrives intact instead of as mojibake, and a batch mixing encodings no longer raises
|
|
32
|
+
`Encoding::CompatibilityError` while being assembled
|
|
33
|
+
- `Response#success?` returns false for a reply containing no records at all, rather than reporting
|
|
34
|
+
success off a vacuously true empty check
|
|
35
|
+
- `Response` now parses the reply as a list of records split on the `[clientid]` header, instead of
|
|
36
|
+
flattening every line into one hash. A `SmBulkSend` reply previously kept only the last message's
|
|
37
|
+
`msgid` and `statuscode`, silently discarding the result of every other message in the batch
|
|
38
|
+
- `Response#error` returns the documented reason for a failing `statuscode` (附錄二). It previously
|
|
39
|
+
read an `Error` field that does not exist in the API, so it was always `nil`
|
|
40
|
+
- `Response#success?` accepts statuscode `0` (預約傳送中), `2` and `4` in addition to `1`. Scheduled
|
|
41
|
+
sends were previously reported as failures. For a batch it is now true only if every record was
|
|
42
|
+
accepted
|
|
43
|
+
- `Response#account_point` is read as a reply-level value rather than being attached to whichever
|
|
44
|
+
record happened to be last
|
|
45
|
+
- `send_sms` now sends the callback URL as `response` and the dedup key as `clientid`, matching the
|
|
46
|
+
Mitake API spec. They were previously sent as `response_url` and `client_id`, which the API ignores,
|
|
47
|
+
so delivery report callbacks and 12-hour duplicate suppression never took effect
|
|
48
|
+
|
|
49
|
+
### Removed
|
|
50
|
+
- Removed `advanced_batch_send` and `advanced_batch_send_with_limit`. They posted to a `SmPost`
|
|
51
|
+
endpoint that does not exist in the Mitake API, and passed recipient numbers and message bodies in
|
|
52
|
+
the query string, which leaked them into access logs and broke on large batches due to URL length
|
|
53
|
+
limits. Use `batch_send`, which implements the same `$$` format against `SmBulkSend`
|
|
54
|
+
- Removed the `charset` option from `send_sms` and `batch_send`. It only labelled the payload and
|
|
55
|
+
never converted it, so `charset: 'BIG5'` sent UTF-8 bytes tagged as Big5, guaranteeing mojibake.
|
|
56
|
+
Requests are now always UTF8 and non-UTF-8 input is transcoded for you
|
|
57
|
+
- Removed `batch_send_with_limit`. The 500 message cap is an API constraint rather than a tuning
|
|
58
|
+
knob, and `batch_send` already applies it
|
|
59
|
+
- Dropped the `faraday-multipart` runtime dependency. The multipart middleware was registered after
|
|
60
|
+
`url_encoded`, so it never handled a request, and the gem never sends file uploads
|
|
9
61
|
|
|
10
62
|
## [2.0.0] - 2025-05-26
|
|
11
63
|
### Changed
|
data/README.md
CHANGED
|
@@ -8,13 +8,17 @@ A Ruby client for the Mitake SMS API, providing a simple and efficient way to se
|
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
- Send single SMS messages
|
|
12
|
-
- Send batch SMS messages with automatic handling of the 500 message API limit
|
|
13
|
-
- UTF-8
|
|
11
|
+
- Send single SMS messages (`SmSend`)
|
|
12
|
+
- Send batch SMS messages (`SmBulkSend`) with automatic handling of the 500 message API limit
|
|
13
|
+
- Always sends UTF-8, transcoding input in other encodings
|
|
14
|
+
- Parses every record of a reply, not just the first
|
|
14
15
|
- Configurable API settings
|
|
15
|
-
- Simple and intuitive API
|
|
16
16
|
- Comprehensive error handling
|
|
17
17
|
|
|
18
|
+
Status queries (`SmQuery`) and cancelling a scheduled send (`SmCancel`) are not
|
|
19
|
+
implemented yet. To learn whether a message reached the handset, have Mitake call
|
|
20
|
+
the `response_url` callback.
|
|
21
|
+
|
|
18
22
|
## Installation
|
|
19
23
|
|
|
20
24
|
Add this line to your application's Gemfile:
|
|
@@ -48,9 +52,14 @@ MitakeSms.configure do |config|
|
|
|
48
52
|
config.username = 'your_username' # Your Mitake SMS API username
|
|
49
53
|
config.password = 'your_password' # Your Mitake SMS API password
|
|
50
54
|
config.api_url = 'https://smsapi.mitake.com.tw/api/mtk/' # Default API URL
|
|
55
|
+
config.timeout = 30 # Read timeout in seconds, default 30
|
|
56
|
+
config.open_timeout = 5 # Connection timeout in seconds, default 5
|
|
51
57
|
end
|
|
52
58
|
```
|
|
53
59
|
|
|
60
|
+
The default `api_url` targets the HTTP API. If your account is provisioned for the
|
|
61
|
+
B2C variant, set the path to `/b2c/mtk/` instead; the endpoints are otherwise identical.
|
|
62
|
+
|
|
54
63
|
### Sending a Single SMS
|
|
55
64
|
|
|
56
65
|
```ruby
|
|
@@ -68,153 +77,129 @@ end
|
|
|
68
77
|
response = MitakeSms.send_sms(
|
|
69
78
|
to: '0912345678',
|
|
70
79
|
text: 'Hello with options!',
|
|
71
|
-
destname: 'John Doe', # Recipient name or integration key value
|
|
72
|
-
response_url: 'https://your-callback-url.com/delivery-reports',
|
|
73
|
-
client_id: 'your-client-reference-id',
|
|
74
|
-
|
|
80
|
+
destname: 'John Doe', # Recipient name or integration key value (sent as `destname`)
|
|
81
|
+
response_url: 'https://your-callback-url.com/delivery-reports', # Sent as `response`
|
|
82
|
+
client_id: 'your-client-reference-id', # Sent as `clientid`, used by Mitake to de-duplicate
|
|
83
|
+
dlvtime: '20250526120000', # Any other documented SmSend field is forwarded as-is
|
|
84
|
+
vldtime: '20250527120000'
|
|
75
85
|
)
|
|
76
86
|
```
|
|
77
87
|
|
|
88
|
+
Requests are always sent as UTF-8. Strings in another encoding are transcoded for
|
|
89
|
+
you, so `text: '中文'.encode('BIG5')` arrives intact.
|
|
90
|
+
|
|
78
91
|
### Sending Multiple SMS in Batch
|
|
79
92
|
|
|
93
|
+
Each message accepts the same fields as a single send. `client_id` is generated for
|
|
94
|
+
you when omitted; Mitake uses it to suppress duplicates within 12 hours.
|
|
95
|
+
|
|
80
96
|
```ruby
|
|
81
97
|
messages = [
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
{
|
|
99
|
+
client_id: 'unique-id-20250525-001', # Optional, auto-generated when absent
|
|
100
|
+
to: '0912345678', # Required
|
|
101
|
+
dlvtime: '20250526120000', # Optional scheduled delivery (YYYYMMDDhhmmss)
|
|
102
|
+
vldtime: '20250527120000', # Optional validity deadline (YYYYMMDDhhmmss)
|
|
103
|
+
destname: '大寶', # Optional recipient name
|
|
104
|
+
response_url: 'https://callback.url', # Optional per-message callback
|
|
105
|
+
text: '這是一則測試簡訊' # Required
|
|
106
|
+
},
|
|
107
|
+
{ to: '0922333444', text: '這是另一則測試簡訊' }
|
|
85
108
|
]
|
|
86
109
|
|
|
87
|
-
# Automatically handles batches according to the Mitake SMS API limit (500 messages per request)
|
|
88
|
-
# If you send more than 500 messages, they will be automatically split into multiple requests
|
|
89
|
-
# Uses UTF-8 encoding by default
|
|
90
110
|
response = MitakeSms.batch_send(messages)
|
|
91
111
|
|
|
92
|
-
#
|
|
93
|
-
response = MitakeSms.batch_send(messages,
|
|
112
|
+
# Any other documented SmBulkSend field is forwarded as a query parameter
|
|
113
|
+
response = MitakeSms.batch_send(messages, smsPointFlag: '1', objectID: 'nightly-run')
|
|
114
|
+
```
|
|
94
115
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
puts "Batch sent successfully!"
|
|
98
|
-
puts "Message ID: #{response.message_id}"
|
|
99
|
-
puts "Remaining points: #{response.account_point}"
|
|
116
|
+
Batches over 500 messages are split automatically, which is the API's per-request
|
|
117
|
+
limit. Under the limit you get one `Response`; over it you get an array of them.
|
|
100
118
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
else
|
|
109
|
-
puts "Batch #{index + 1} failed: #{batch_response.error}"
|
|
110
|
-
end
|
|
119
|
+
```ruby
|
|
120
|
+
Array(response).each_with_index do |batch, index|
|
|
121
|
+
if batch.success?
|
|
122
|
+
puts "Batch #{index + 1} sent. Message IDs: #{batch.message_ids.join(', ')}"
|
|
123
|
+
puts "Remaining points: #{batch.account_point}"
|
|
124
|
+
else
|
|
125
|
+
puts "Batch #{index + 1} failed: #{batch.error}"
|
|
111
126
|
end
|
|
112
|
-
else
|
|
113
|
-
puts "Failed to send batch: #{response.error}"
|
|
114
127
|
end
|
|
115
128
|
```
|
|
116
129
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# Create a large batch of messages
|
|
121
|
-
messages = (1..1000).map do |i|
|
|
122
|
-
{ to: '0912345678', text: "Message #{i}" }
|
|
123
|
-
end
|
|
130
|
+
Line breaks in `text` are converted to ASCII code 6, as the API requires, so
|
|
131
|
+
`"First line\nSecond line"` renders as two lines on the handset. Messages longer
|
|
132
|
+
than one SMS are split by Mitake unless your account has long-message permission.
|
|
124
133
|
|
|
125
|
-
|
|
126
|
-
# However, you can set a lower limit if needed for your use case
|
|
127
|
-
# This will split into batches of 300 messages each
|
|
128
|
-
# Uses UTF-8 encoding by default
|
|
129
|
-
responses = MitakeSms.batch_send_with_limit(messages, 300)
|
|
134
|
+
#### Delimiters in batch fields
|
|
130
135
|
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
The batch wire format separates fields with `$$` and rows with a newline. Either
|
|
137
|
+
one inside `client_id`, `to`, `dlvtime`, `vldtime`, `destname` or `response_url`
|
|
138
|
+
would shift every field after it, so `batch_send` raises `ArgumentError` naming
|
|
139
|
+
the offending message and field instead of sending a corrupted row:
|
|
133
140
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
puts "Batch #{index + 1} sent successfully!"
|
|
138
|
-
else
|
|
139
|
-
puts "Batch #{index + 1} failed: #{batch_response.error}"
|
|
140
|
-
end
|
|
141
|
-
end
|
|
141
|
+
```ruby
|
|
142
|
+
MitakeSms.batch_send([{ to: '0912345678', destname: 'A$$B', text: 'hi' }])
|
|
143
|
+
# => ArgumentError: messages[0][:destname] must not contain "$$" or a line break, ...
|
|
142
144
|
```
|
|
143
145
|
|
|
144
|
-
|
|
146
|
+
`text` is exempt because it is the last field on the row, so a message body may
|
|
147
|
+
contain `$$` freely. Mitake's documentation does not define an escape sequence,
|
|
148
|
+
though, so if a body containing `$$` ever comes back truncated, send it through
|
|
149
|
+
`send_sms` instead.
|
|
150
|
+
|
|
151
|
+
### Reading a Response
|
|
145
152
|
|
|
146
|
-
|
|
153
|
+
A batch reply contains one record per message, each introduced by a `[clientid]` line,
|
|
154
|
+
followed by an `AccountPoint` shared by the whole reply. A single send may omit the
|
|
155
|
+
`[clientid]` header; either shape is parsed the same way. `MitakeSms::Response` keeps
|
|
156
|
+
every record rather than collapsing them:
|
|
147
157
|
|
|
148
158
|
```ruby
|
|
149
|
-
#
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
to: '0922333444',
|
|
163
|
-
text: '這是另一則測試簡訊'
|
|
164
|
-
# Other fields are optional
|
|
165
|
-
}
|
|
166
|
-
]
|
|
159
|
+
response.success? # true only if there is at least one record and every one was accepted
|
|
160
|
+
response.error? # the inverse of success?
|
|
161
|
+
response.error # nil on success, otherwise the documented reason for each failed record
|
|
162
|
+
response.code # statuscode of the first record
|
|
163
|
+
response.message_id # msgid of the first record
|
|
164
|
+
response.message_ids # msgid of every record, for batch sends
|
|
165
|
+
response.client_id # clientid of the first record
|
|
166
|
+
response.account_point # remaining points, shared by the whole reply
|
|
167
|
+
response.duplicate? # true when Mitake suppressed a resend of the same clientid
|
|
168
|
+
response.sms_point # points deducted, only present when smsPointFlag=1 was sent
|
|
169
|
+
response.records # raw per-message hashes, if you need a field not listed above
|
|
170
|
+
response.raw_response # the untouched reply body
|
|
171
|
+
```
|
|
167
172
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
# - If not provided, a unique ID will be automatically generated using timestamp and random values
|
|
171
|
-
# - For custom tracking, you can provide your own unique ClientID
|
|
172
|
-
#
|
|
173
|
-
# Note about message text formatting:
|
|
174
|
-
# - If your message text contains line breaks (\n), they will be automatically converted
|
|
175
|
-
# to ASCII code 6 as required by the Mitake API
|
|
176
|
-
# - Example: "First line\nSecond line" will be properly displayed with a line break on the recipient's device
|
|
177
|
-
# - Special characters like '&' are automatically URL encoded to ensure proper transmission
|
|
178
|
-
# - Long messages will be automatically split into multiple SMS messages if your account doesn't
|
|
179
|
-
# have long message permissions
|
|
180
|
-
|
|
181
|
-
# Send using batch_send (automatically handles the advanced format)
|
|
182
|
-
response = MitakeSms.batch_send(messages)
|
|
173
|
+
Accepted status codes are `0`, `1`, `2` and `4`. An empty or unparseable body is not a
|
|
174
|
+
success, and `error` reports `'Empty or unparseable response'` for it.
|
|
183
175
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
puts "Message ID: #{response.message_id}"
|
|
188
|
-
puts "Remaining points: #{response.account_point}"
|
|
189
|
-
elsif response.is_a?(Array)
|
|
190
|
-
response.each_with_index do |batch_response, index|
|
|
191
|
-
if batch_response.success?
|
|
192
|
-
puts "Batch #{index + 1} sent successfully!"
|
|
193
|
-
else
|
|
194
|
-
puts "Batch #{index + 1} failed: #{batch_response.error}"
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
```
|
|
176
|
+
Note that `statuscode=0` means 預約傳送中 (queued for scheduled delivery) and counts as a success.
|
|
177
|
+
Failures are the numeric codes `5`–`9` and the letter codes documented in 附錄二, such as `e`
|
|
178
|
+
(帳號、密碼錯誤) or `v` (無效的手機號碼).
|
|
199
179
|
|
|
200
180
|
### Error Handling
|
|
201
181
|
|
|
202
|
-
|
|
182
|
+
HTTP status codes are mapped to specific error classes, while transport failures are
|
|
183
|
+
raised as their original `Faraday::Error` subclass so you can retry on timeouts alone:
|
|
203
184
|
|
|
204
185
|
```ruby
|
|
205
186
|
begin
|
|
206
|
-
response = MitakeSms.send_sms('
|
|
187
|
+
response = MitakeSms.send_sms(to: '0912345678', text: 'test')
|
|
207
188
|
rescue MitakeSms::Client::AuthenticationError => e
|
|
208
189
|
puts "Authentication failed: #{e.message}"
|
|
209
190
|
rescue MitakeSms::Client::InvalidRequestError => e
|
|
210
191
|
puts "Invalid request: #{e.message}"
|
|
211
192
|
rescue MitakeSms::Client::ServerError => e
|
|
212
193
|
puts "Server error: #{e.message}"
|
|
213
|
-
rescue
|
|
214
|
-
puts "
|
|
194
|
+
rescue Faraday::TimeoutError => e
|
|
195
|
+
puts "Timed out, safe to retry: #{e.message}"
|
|
215
196
|
end
|
|
216
197
|
```
|
|
217
198
|
|
|
199
|
+
Note that `SmBulkSend` takes the username and password as query string parameters,
|
|
200
|
+
which the API mandates and the gem cannot avoid. They are protected in transit by
|
|
201
|
+
HTTPS, but if you log outbound request URLs, filter them at that layer.
|
|
202
|
+
|
|
218
203
|
## Development
|
|
219
204
|
|
|
220
205
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
@@ -225,6 +210,7 @@ To install this gem onto your local machine, run `bundle exec rake install`.
|
|
|
225
210
|
|
|
226
211
|
Bug reports and pull requests are welcome on GitHub at https://github.com/7a6163/mitake_sms.
|
|
227
212
|
|
|
213
|
+
|
|
228
214
|
## License
|
|
229
215
|
|
|
230
216
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/mitake_sms/client.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'faraday'
|
|
4
|
-
require '
|
|
4
|
+
require 'securerandom'
|
|
5
5
|
require_relative 'configuration'
|
|
6
6
|
require_relative 'response'
|
|
7
7
|
|
|
@@ -12,6 +12,21 @@ module MitakeSms
|
|
|
12
12
|
class InvalidRequestError < Error; end
|
|
13
13
|
class ServerError < Error; end
|
|
14
14
|
|
|
15
|
+
# The Mitake API represents a line break inside smbody as ASCII code 6.
|
|
16
|
+
LINE_BREAK = 6.chr
|
|
17
|
+
|
|
18
|
+
BATCH_LIMIT = 500
|
|
19
|
+
|
|
20
|
+
# The API also accepts Big5, but it only labels the payload and never
|
|
21
|
+
# converts it, so sending anything other than UTF8 just invites mojibake.
|
|
22
|
+
CHARSET = 'UTF8'
|
|
23
|
+
|
|
24
|
+
FIELD_SEPARATOR = '$$'
|
|
25
|
+
|
|
26
|
+
# Every SmBulkSend field except smbody, in wire order. A '$$' or a line
|
|
27
|
+
# break in any of these shifts the remaining fields of the row.
|
|
28
|
+
STRUCTURAL_FIELDS = %i[client_id to dlvtime vldtime destname response_url].freeze
|
|
29
|
+
|
|
15
30
|
# Initialize a new MitakeSms::Client
|
|
16
31
|
# @param config [MitakeSms::Configuration] configuration object
|
|
17
32
|
def initialize(config = nil)
|
|
@@ -25,220 +40,97 @@ module MitakeSms
|
|
|
25
40
|
# @param destname [String] recipient name or key value for system integration (optional)
|
|
26
41
|
# @param response_url [String] callback URL for delivery reports (optional)
|
|
27
42
|
# @param client_id [String] client reference ID (optional)
|
|
28
|
-
# @param
|
|
29
|
-
#
|
|
43
|
+
# @param options [Hash] any other documented SmSend field, such as
|
|
44
|
+
# :dlvtime, :vldtime, :objectID or :smsPointFlag
|
|
30
45
|
# @return [MitakeSms::Response] response object
|
|
31
|
-
def send_sms(to:, text:, destname: nil, response_url: nil, client_id: nil,
|
|
32
|
-
require 'uri'
|
|
33
|
-
|
|
34
|
-
# Create options hash with only non-nil values
|
|
35
|
-
param_options = {}
|
|
36
|
-
param_options[:destname] = destname if destname
|
|
37
|
-
param_options[:response_url] = response_url if response_url
|
|
38
|
-
param_options[:client_id] = client_id if client_id
|
|
39
|
-
|
|
40
|
-
# Replace any newline characters with ASCII code 6 (ACK)
|
|
41
|
-
# This is required by the Mitake API to represent line breaks
|
|
42
|
-
processed_text = text.to_s.gsub("\n", 6.chr)
|
|
43
|
-
|
|
44
|
-
# Prepare query parameters - only CharsetURL is sent as query parameter
|
|
45
|
-
query_params = {
|
|
46
|
-
CharsetURL: charset
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
# Prepare form parameters - all other parameters are sent in the POST body
|
|
46
|
+
def send_sms(to:, text:, destname: nil, response_url: nil, client_id: nil, **options)
|
|
50
47
|
form_params = {
|
|
51
48
|
username: @config.username,
|
|
52
49
|
password: @config.password,
|
|
53
50
|
dstaddr: to,
|
|
54
|
-
smbody:
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
smbody: normalize_body(text)
|
|
52
|
+
}
|
|
53
|
+
form_params[:destname] = destname if destname
|
|
54
|
+
form_params[:response] = response_url if response_url
|
|
55
|
+
form_params[:clientid] = client_id if client_id
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
req.
|
|
62
|
-
req.body = form_params
|
|
57
|
+
perform_request('SmSend', params: { CharsetURL: CHARSET }) do |req|
|
|
58
|
+
req.body = form_params.merge(options)
|
|
63
59
|
end
|
|
64
|
-
|
|
65
|
-
handle_response(response)
|
|
66
60
|
end
|
|
67
61
|
|
|
68
|
-
# Send multiple SMS in a single request
|
|
62
|
+
# Send multiple SMS in a single request, splitting at the API's 500 message limit
|
|
69
63
|
# @param messages [Array<Hash>] array of message hashes
|
|
70
|
-
# Each hash
|
|
71
|
-
#
|
|
72
|
-
# @
|
|
73
|
-
#
|
|
64
|
+
# Each hash requires :to and :text, and may include :client_id, :dlvtime,
|
|
65
|
+
# :vldtime, :destname and :response_url
|
|
66
|
+
# @param options [Hash] any other documented SmBulkSend field, such as
|
|
67
|
+
# :objectID or :smsPointFlag
|
|
68
|
+
# @raise [ArgumentError] if a field other than :text contains '$$' or a line break
|
|
74
69
|
# @return [MitakeSms::Response, Array<MitakeSms::Response>] response object or array of response objects if batch was split
|
|
75
70
|
def batch_send(messages, options = {})
|
|
76
|
-
|
|
77
|
-
# Automatically split larger batches into multiple requests of 500 messages each
|
|
78
|
-
batch_send_with_limit(messages, 500, options)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Send multiple SMS in a single request with a limit per request
|
|
82
|
-
# @param messages [Array<Hash>] array of message hashes
|
|
83
|
-
# Each hash should contain :to and :text keys, and can include :from, :response_url, :client_id
|
|
84
|
-
# @param limit [Integer] maximum number of messages per request (default: 500)
|
|
85
|
-
# @param options [Hash] additional options
|
|
86
|
-
# @option options [String] :charset character encoding, defaults to 'UTF8'
|
|
87
|
-
# @option options [Boolean] :skip_encoding skip URL encoding (for tests)
|
|
88
|
-
# @return [MitakeSms::Response, Array<MitakeSms::Response>] response object or array of response objects if batch was split
|
|
89
|
-
def batch_send_with_limit(messages, limit = 500, options = {})
|
|
90
|
-
charset = options[:charset] || 'UTF8'
|
|
91
|
-
|
|
92
|
-
# If messages count is within the limit, use the regular batch send
|
|
93
|
-
return send_batch(messages, charset, options) if messages.size <= limit
|
|
94
|
-
|
|
95
|
-
# Otherwise, split into batches of the specified limit
|
|
96
|
-
responses = []
|
|
97
|
-
messages.each_slice(limit) do |batch|
|
|
98
|
-
responses << send_batch(batch, charset, options)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Return array of responses
|
|
102
|
-
responses
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# Send multiple SMS in a single request using advanced format
|
|
106
|
-
# @param messages [Array<Hash>] array of message hashes with advanced options
|
|
107
|
-
# Each hash can contain the following keys:
|
|
108
|
-
# - :client_id [String] client reference ID (required)
|
|
109
|
-
# - :to [String] recipient phone number (required)
|
|
110
|
-
# - :dlvtime [String] delivery time in format YYYYMMDDHHMMSS (optional)
|
|
111
|
-
# - :vldtime [String] valid until time in format YYYYMMDDHHMMSS (optional)
|
|
112
|
-
# - :dest_name [String] recipient name (optional)
|
|
113
|
-
# - :response [String] callback URL for delivery reports (optional)
|
|
114
|
-
# - :text [String] message content (required)
|
|
115
|
-
# @param options [Hash] additional options
|
|
116
|
-
# @option options [String] :charset character encoding, defaults to 'UTF8'
|
|
117
|
-
# @option options [Boolean] :skip_encoding skip URL encoding (for tests)
|
|
118
|
-
# @return [MitakeSms::Response, Array<MitakeSms::Response>] response object or array of response objects if batch was split
|
|
119
|
-
def advanced_batch_send(messages, options = {})
|
|
120
|
-
# Mitake SMS API has a limit of 500 messages per request
|
|
121
|
-
# Automatically split larger batches into multiple requests of 500 messages each
|
|
122
|
-
advanced_batch_send_with_limit(messages, 500, options)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Send multiple SMS in a single request with a limit per request using advanced format
|
|
126
|
-
# @param messages [Array<Hash>] array of message hashes with advanced options
|
|
127
|
-
# @param limit [Integer] maximum number of messages per request (default: 500)
|
|
128
|
-
# @param options [Hash] additional options
|
|
129
|
-
# @option options [String] :charset character encoding, defaults to 'UTF8'
|
|
130
|
-
# @option options [Boolean] :skip_encoding skip URL encoding (for tests)
|
|
131
|
-
# @return [MitakeSms::Response, Array<MitakeSms::Response>] response object or array of response objects if batch was split
|
|
132
|
-
def advanced_batch_send_with_limit(messages, limit = 500, options = {})
|
|
133
|
-
charset = options[:charset] || 'UTF8'
|
|
134
|
-
|
|
135
|
-
# If messages count is within the limit, use the regular batch send
|
|
136
|
-
return send_advanced_batch(messages, charset, options) if messages.size <= limit
|
|
71
|
+
messages.each_with_index { |msg, index| validate_row!(msg, index) }
|
|
137
72
|
|
|
138
|
-
|
|
139
|
-
responses = []
|
|
140
|
-
messages.each_slice(limit) do |batch|
|
|
141
|
-
responses << send_advanced_batch(batch, charset, options)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Return array of responses
|
|
145
|
-
responses
|
|
73
|
+
batch_send_with_limit(messages, BATCH_LIMIT, options)
|
|
146
74
|
end
|
|
147
75
|
|
|
148
76
|
private
|
|
149
77
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
# @param charset [String] character encoding, defaults to 'UTF8'
|
|
153
|
-
# @param options [Hash] additional options
|
|
154
|
-
# @return [MitakeSms::Response] response object
|
|
155
|
-
def send_batch(batch, charset = 'UTF8', options = {})
|
|
156
|
-
require 'uri'
|
|
157
|
-
|
|
158
|
-
# Format each message according to the advanced format
|
|
159
|
-
# ClientID $$ dstaddr $$ dlvtime $$ vldtime $$ destname $$ response $$ smbody
|
|
160
|
-
data = batch.map do |msg|
|
|
161
|
-
# ClientID is required and must be unique
|
|
162
|
-
# If not provided, generate a unique ID
|
|
163
|
-
client_id = msg[:client_id]
|
|
164
|
-
if client_id.nil? || client_id.empty?
|
|
165
|
-
client_id = generate_unique_client_id
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
to = msg[:to]
|
|
169
|
-
dlvtime = msg[:dlvtime] || ''
|
|
170
|
-
vldtime = msg[:vldtime] || ''
|
|
171
|
-
dest_name = msg[:destname] || ''
|
|
172
|
-
response_url = msg[:response_url] || ''
|
|
78
|
+
def batch_send_with_limit(messages, limit, options)
|
|
79
|
+
return send_batch(messages, options) if messages.size <= limit
|
|
173
80
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
processed_text = msg[:text].to_s.gsub("\n", 6.chr)
|
|
177
|
-
|
|
178
|
-
# Format according to API documentation: ClientID $$ dstaddr $$ dlvtime $$ vldtime $$ destname $$ response $$ smbody
|
|
179
|
-
[client_id, to, dlvtime, vldtime, dest_name, response_url, processed_text].join('$$')
|
|
180
|
-
end.join("\n")
|
|
81
|
+
messages.each_slice(limit).map { |batch| send_batch(batch, options) }
|
|
82
|
+
end
|
|
181
83
|
|
|
182
|
-
|
|
84
|
+
def send_batch(batch, options)
|
|
183
85
|
query_params = {
|
|
184
86
|
username: @config.username,
|
|
185
87
|
password: @config.password,
|
|
186
|
-
Encoding_PostIn:
|
|
187
|
-
}
|
|
88
|
+
Encoding_PostIn: CHARSET
|
|
89
|
+
}.merge(options)
|
|
188
90
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
req.params = query_params
|
|
192
|
-
req.body = data
|
|
91
|
+
perform_request('SmBulkSend', params: query_params) do |req|
|
|
92
|
+
req.body = batch.map { |msg| format_batch_row(msg) }.join("\n")
|
|
193
93
|
req.headers['Content-Type'] = 'text/plain'
|
|
194
94
|
end
|
|
195
|
-
|
|
196
|
-
handle_response(response)
|
|
197
95
|
end
|
|
198
96
|
|
|
199
|
-
#
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
client_id = generate_unique_client_id
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
to = msg[:to]
|
|
218
|
-
dlvtime = msg[:dlvtime] || ''
|
|
219
|
-
vldtime = msg[:vldtime] || ''
|
|
220
|
-
dest_name = msg[:dest_name] || ''
|
|
221
|
-
response_url = msg[:response] || ''
|
|
97
|
+
# ClientID $$ dstaddr $$ dlvtime $$ vldtime $$ destname $$ response $$ smbody
|
|
98
|
+
def format_batch_row(msg)
|
|
99
|
+
client_id = msg[:client_id]
|
|
100
|
+
client_id = generate_unique_client_id if client_id.to_s.empty?
|
|
101
|
+
|
|
102
|
+
[
|
|
103
|
+
client_id,
|
|
104
|
+
msg[:to],
|
|
105
|
+
msg[:dlvtime],
|
|
106
|
+
msg[:vldtime],
|
|
107
|
+
msg[:destname],
|
|
108
|
+
msg[:response_url],
|
|
109
|
+
normalize_body(msg[:text])
|
|
110
|
+
].join(FIELD_SEPARATOR)
|
|
111
|
+
end
|
|
222
112
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
113
|
+
def validate_row!(msg, index)
|
|
114
|
+
STRUCTURAL_FIELDS.each do |field|
|
|
115
|
+
value = msg[field].to_s
|
|
116
|
+
next unless value.include?(FIELD_SEPARATOR) || value.match?(/[\r\n]/)
|
|
226
117
|
|
|
227
|
-
|
|
228
|
-
|
|
118
|
+
raise ArgumentError,
|
|
119
|
+
"messages[#{index}][:#{field}] must not contain #{FIELD_SEPARATOR.inspect} or a line break, " \
|
|
120
|
+
'because both are SmBulkSend delimiters'
|
|
121
|
+
end
|
|
122
|
+
end
|
|
229
123
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
Encoding_PostIn: charset
|
|
236
|
-
}
|
|
124
|
+
# Mitake expects UTF8 bytes with ASCII code 6 standing in for a line break.
|
|
125
|
+
# Transcoding also keeps a batch of mixed-encoding strings from failing to join.
|
|
126
|
+
def normalize_body(text)
|
|
127
|
+
text.to_s.encode(Encoding::UTF_8).gsub(/\r\n?|\n/, LINE_BREAK)
|
|
128
|
+
end
|
|
237
129
|
|
|
238
|
-
|
|
239
|
-
response = @connection.post(
|
|
240
|
-
req.params =
|
|
241
|
-
req
|
|
130
|
+
def perform_request(endpoint, params:)
|
|
131
|
+
response = @connection.post(endpoint) do |req|
|
|
132
|
+
req.params = params
|
|
133
|
+
yield req
|
|
242
134
|
end
|
|
243
135
|
|
|
244
136
|
handle_response(response)
|
|
@@ -247,7 +139,6 @@ module MitakeSms
|
|
|
247
139
|
def build_connection
|
|
248
140
|
Faraday.new(url: @config.api_url) do |conn|
|
|
249
141
|
conn.request :url_encoded
|
|
250
|
-
conn.request :multipart
|
|
251
142
|
conn.adapter Faraday.default_adapter
|
|
252
143
|
conn.options.timeout = @config.timeout
|
|
253
144
|
conn.options.open_timeout = @config.open_timeout
|
|
@@ -269,15 +160,10 @@ module MitakeSms
|
|
|
269
160
|
end
|
|
270
161
|
end
|
|
271
162
|
|
|
272
|
-
#
|
|
273
|
-
#
|
|
163
|
+
# Mitake uses the client ID to suppress duplicate sends within 12 hours,
|
|
164
|
+
# so it has to be unique per message rather than merely random.
|
|
274
165
|
def generate_unique_client_id
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
# Generate a unique ID using timestamp (to milliseconds) and a random UUID portion
|
|
278
|
-
timestamp = Time.now.strftime('%Y%m%d%H%M%S%L')
|
|
279
|
-
random_part = SecureRandom.uuid.gsub('-', '')[0, 8]
|
|
280
|
-
"#{timestamp}-#{random_part}"
|
|
166
|
+
"#{Time.now.strftime('%Y%m%d%H%M%S%L')}-#{SecureRandom.uuid.delete('-')[0, 8]}"
|
|
281
167
|
end
|
|
282
168
|
end
|
|
283
169
|
end
|
data/lib/mitake_sms/response.rb
CHANGED
|
@@ -2,37 +2,128 @@
|
|
|
2
2
|
|
|
3
3
|
module MitakeSms
|
|
4
4
|
class Response
|
|
5
|
-
|
|
5
|
+
# 附錄一 / 附錄二:statuscode 對應說明
|
|
6
|
+
STATUS_MESSAGES = {
|
|
7
|
+
'0' => '預約傳送中',
|
|
8
|
+
'1' => '已送達業者',
|
|
9
|
+
'2' => '已送達業者',
|
|
10
|
+
'4' => '已送達手機',
|
|
11
|
+
'5' => '內容有錯誤',
|
|
12
|
+
'6' => '門號有錯誤',
|
|
13
|
+
'7' => '簡訊已停用',
|
|
14
|
+
'8' => '逾時無送達',
|
|
15
|
+
'9' => '預約已取消',
|
|
16
|
+
'*' => '系統發生錯誤,請聯絡三竹資訊窗口人員',
|
|
17
|
+
'a' => '簡訊發送功能暫時停止服務,請稍候再試',
|
|
18
|
+
'b' => '簡訊發送功能暫時停止服務,請稍候再試',
|
|
19
|
+
'c' => '請輸入帳號',
|
|
20
|
+
'd' => '請輸入密碼',
|
|
21
|
+
'e' => '帳號、密碼錯誤',
|
|
22
|
+
'f' => '帳號已過期',
|
|
23
|
+
'h' => '帳號已被停用',
|
|
24
|
+
'k' => '無效的連線位址',
|
|
25
|
+
'l' => '帳號已達到同時連線數上限',
|
|
26
|
+
'm' => '必須變更密碼,在變更密碼前,無法使用簡訊發送服務',
|
|
27
|
+
'n' => '密碼已逾期,在變更密碼前,將無法使用簡訊發送服務',
|
|
28
|
+
'p' => '沒有權限使用外部Http程式',
|
|
29
|
+
'r' => '系統暫停服務,請稍後再試',
|
|
30
|
+
's' => '帳務處理失敗,無法發送簡訊',
|
|
31
|
+
't' => '簡訊已過期',
|
|
32
|
+
'u' => '簡訊內容不得為空白',
|
|
33
|
+
'v' => '無效的手機號碼',
|
|
34
|
+
'w' => '查詢筆數超過上限',
|
|
35
|
+
'x' => '發送檔案過大,無法發送簡訊',
|
|
36
|
+
'y' => '參數錯誤',
|
|
37
|
+
'z' => '查無資料'
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
# 簡訊已被三竹收下或已送達。其餘 statuscode 皆視為失敗。
|
|
41
|
+
ACCEPTED_STATUS_CODES = %w[0 1 2 4].freeze
|
|
42
|
+
|
|
43
|
+
attr_reader :raw_response, :records, :account_point
|
|
6
44
|
|
|
7
45
|
def initialize(raw_response)
|
|
8
46
|
@raw_response = raw_response
|
|
9
|
-
|
|
47
|
+
@records = []
|
|
48
|
+
@account_point = nil
|
|
49
|
+
parse(raw_response)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def code
|
|
53
|
+
first_record['statuscode']
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def message_id
|
|
57
|
+
first_record['msgid']
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def client_id
|
|
61
|
+
first_record['clientid']
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def sms_point
|
|
65
|
+
first_record['smsPoint']
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def duplicate?
|
|
69
|
+
first_record['Duplicate'] == 'Y'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def message_ids
|
|
73
|
+
@records.map { |record| record['msgid'] }.compact
|
|
10
74
|
end
|
|
11
75
|
|
|
12
76
|
def success?
|
|
13
|
-
|
|
77
|
+
return false if @records.empty?
|
|
78
|
+
|
|
79
|
+
@records.all? { |record| accepted?(record) }
|
|
14
80
|
end
|
|
15
81
|
|
|
16
82
|
def error?
|
|
17
83
|
!success?
|
|
18
84
|
end
|
|
19
85
|
|
|
86
|
+
def error
|
|
87
|
+
return nil if success?
|
|
88
|
+
|
|
89
|
+
failures = @records.reject { |record| accepted?(record) }
|
|
90
|
+
return 'Empty or unparseable response' if failures.empty?
|
|
91
|
+
|
|
92
|
+
failures.map { |record| describe(record) }.join('; ')
|
|
93
|
+
end
|
|
94
|
+
|
|
20
95
|
private
|
|
21
96
|
|
|
22
|
-
def
|
|
97
|
+
def first_record
|
|
98
|
+
@records.first || {}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def accepted?(record)
|
|
102
|
+
ACCEPTED_STATUS_CODES.include?(record['statuscode'])
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def describe(record)
|
|
106
|
+
status = record['statuscode']
|
|
107
|
+
description = "#{status || '?'} #{STATUS_MESSAGES.fetch(status, '未知的狀態')}"
|
|
108
|
+
record['msgid'] ? "#{record['msgid']}: #{description}" : description
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def parse(response)
|
|
23
112
|
return unless response.is_a?(String)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
113
|
+
|
|
114
|
+
response.each_line do |raw_line|
|
|
115
|
+
case raw_line.strip
|
|
116
|
+
# 每筆回覆以 [clientid] 開頭,例如 [1] 或 [my-guid]
|
|
117
|
+
when /\A\[(.*)\]\z/
|
|
118
|
+
@records << { 'clientid' => Regexp.last_match(1) }
|
|
119
|
+
# AccountPoint 是整批回覆共用的餘額,不屬於任何單筆簡訊
|
|
120
|
+
when /\AAccountPoint=(.*)\z/
|
|
121
|
+
@account_point = Regexp.last_match(1)
|
|
122
|
+
when /\A([^=]+)=(.*)\z/
|
|
123
|
+
@records << {} if @records.empty?
|
|
124
|
+
@records.last[Regexp.last_match(1)] = Regexp.last_match(2)
|
|
125
|
+
end
|
|
30
126
|
end
|
|
31
|
-
|
|
32
|
-
@code = @parsed_response['statuscode']
|
|
33
|
-
@message_id = @parsed_response['msgid']
|
|
34
|
-
@account_point = @parsed_response['AccountPoint']
|
|
35
|
-
@error = @parsed_response['Error']
|
|
36
127
|
end
|
|
37
128
|
end
|
|
38
129
|
end
|
data/lib/mitake_sms/version.rb
CHANGED
data/lib/mitake_sms.rb
CHANGED
|
@@ -41,18 +41,15 @@ module MitakeSms
|
|
|
41
41
|
# @param destname [String] recipient name or key value for system integration (optional)
|
|
42
42
|
# @param response_url [String] callback URL for delivery reports (optional)
|
|
43
43
|
# @param client_id [String] client reference ID (optional)
|
|
44
|
-
# @param
|
|
45
|
-
# @param options [Hash] additional options (optional)
|
|
44
|
+
# @param options [Hash] any other documented SmSend field
|
|
46
45
|
# @return [MitakeSms::Response] response object
|
|
47
|
-
def send_sms(to:, text:, destname: nil, response_url: nil, client_id: nil,
|
|
48
|
-
# Forward all parameters to the client method using named parameters
|
|
46
|
+
def send_sms(to:, text:, destname: nil, response_url: nil, client_id: nil, **options)
|
|
49
47
|
client.send_sms(
|
|
50
48
|
to: to,
|
|
51
49
|
text: text,
|
|
52
50
|
destname: destname,
|
|
53
51
|
response_url: response_url,
|
|
54
52
|
client_id: client_id,
|
|
55
|
-
charset: charset,
|
|
56
53
|
**options
|
|
57
54
|
)
|
|
58
55
|
end
|
|
@@ -61,45 +58,13 @@ module MitakeSms
|
|
|
61
58
|
# The Mitake SMS API has a limit of 500 messages per request
|
|
62
59
|
# If more than 500 messages are provided, they will be automatically split into multiple requests
|
|
63
60
|
# @param messages [Array<Hash>] array of message hashes
|
|
64
|
-
# Each hash
|
|
65
|
-
#
|
|
66
|
-
# @
|
|
67
|
-
|
|
61
|
+
# Each hash requires :to and :text, and may include :client_id, :dlvtime,
|
|
62
|
+
# :vldtime, :destname and :response_url
|
|
63
|
+
# @param options [Hash] any other documented SmBulkSend field
|
|
64
|
+
# @raise [ArgumentError] if a field other than :text contains '$$' or a line break
|
|
68
65
|
# @return [MitakeSms::Response, Array<MitakeSms::Response>] response object or array of response objects if batch was split
|
|
69
66
|
def batch_send(messages, options = {})
|
|
70
|
-
client.
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Send multiple SMS messages in a single request with a limit per request
|
|
74
|
-
# @param messages [Array<Hash>] array of message hashes
|
|
75
|
-
# Each hash should contain :to and :text keys, and can include :from, :response_url, :client_id
|
|
76
|
-
# @param limit [Integer] maximum number of messages per request (default: 500)
|
|
77
|
-
# @param options [Hash] additional options
|
|
78
|
-
# @option options [String] :charset character encoding, defaults to 'UTF8'
|
|
79
|
-
|
|
80
|
-
# @return [MitakeSms::Response, Array<MitakeSms::Response>] response object or array of response objects if batch was split
|
|
81
|
-
def batch_send_with_limit(messages, limit = 500, options = {})
|
|
82
|
-
client.batch_send_with_limit(messages, limit, options)
|
|
67
|
+
client.batch_send(messages, options)
|
|
83
68
|
end
|
|
84
|
-
|
|
85
|
-
# Send multiple SMS messages in a single request using advanced format
|
|
86
|
-
# @param messages [Array<Hash>] array of message hashes with advanced options
|
|
87
|
-
# Each hash can contain the following keys:
|
|
88
|
-
# - :client_id [String] client reference ID (optional)
|
|
89
|
-
# - :to [String] recipient phone number (required)
|
|
90
|
-
# - :dlvtime [String] delivery time in format YYYYMMDDHHMMSS (optional)
|
|
91
|
-
# - :vldtime [String] valid until time in format YYYYMMDDHHMMSS (optional)
|
|
92
|
-
# - :dest_name [String] recipient name (optional)
|
|
93
|
-
# - :response [String] callback URL for delivery reports (optional)
|
|
94
|
-
# - :text [String] message content (required)
|
|
95
|
-
# @param options [Hash] additional options
|
|
96
|
-
# @option options [String] :charset character encoding, defaults to 'UTF8'
|
|
97
|
-
|
|
98
|
-
# @return [MitakeSms::Response, Array<MitakeSms::Response>] response object or array of response objects if batch was split
|
|
99
|
-
def advanced_batch_send(messages, options = {})
|
|
100
|
-
client.advanced_batch_send(messages, options)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
|
|
104
69
|
end
|
|
105
70
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mitake_sms
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zac
|
|
@@ -15,7 +15,7 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 2.14.3
|
|
19
19
|
- - "<"
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
21
|
version: '3.0'
|
|
@@ -25,37 +25,17 @@ dependencies:
|
|
|
25
25
|
requirements:
|
|
26
26
|
- - ">="
|
|
27
27
|
- !ruby/object:Gem::Version
|
|
28
|
-
version:
|
|
28
|
+
version: 2.14.3
|
|
29
29
|
- - "<"
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
31
|
version: '3.0'
|
|
32
|
-
- !ruby/object:Gem::Dependency
|
|
33
|
-
name: faraday-multipart
|
|
34
|
-
requirement: !ruby/object:Gem::Requirement
|
|
35
|
-
requirements:
|
|
36
|
-
- - ">="
|
|
37
|
-
- !ruby/object:Gem::Version
|
|
38
|
-
version: '0.1'
|
|
39
|
-
- - "<"
|
|
40
|
-
- !ruby/object:Gem::Version
|
|
41
|
-
version: '2.0'
|
|
42
|
-
type: :runtime
|
|
43
|
-
prerelease: false
|
|
44
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
45
|
-
requirements:
|
|
46
|
-
- - ">="
|
|
47
|
-
- !ruby/object:Gem::Version
|
|
48
|
-
version: '0.1'
|
|
49
|
-
- - "<"
|
|
50
|
-
- !ruby/object:Gem::Version
|
|
51
|
-
version: '2.0'
|
|
52
32
|
- !ruby/object:Gem::Dependency
|
|
53
33
|
name: dry-configurable
|
|
54
34
|
requirement: !ruby/object:Gem::Requirement
|
|
55
35
|
requirements:
|
|
56
36
|
- - ">="
|
|
57
37
|
- !ruby/object:Gem::Version
|
|
58
|
-
version:
|
|
38
|
+
version: 1.4.0
|
|
59
39
|
- - "<"
|
|
60
40
|
- !ruby/object:Gem::Version
|
|
61
41
|
version: '2.0'
|
|
@@ -65,7 +45,7 @@ dependencies:
|
|
|
65
45
|
requirements:
|
|
66
46
|
- - ">="
|
|
67
47
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
48
|
+
version: 1.4.0
|
|
69
49
|
- - "<"
|
|
70
50
|
- !ruby/object:Gem::Version
|
|
71
51
|
version: '2.0'
|
|
@@ -131,28 +111,28 @@ dependencies:
|
|
|
131
111
|
requirements:
|
|
132
112
|
- - "~>"
|
|
133
113
|
- !ruby/object:Gem::Version
|
|
134
|
-
version: 0.
|
|
114
|
+
version: 1.0.3
|
|
135
115
|
type: :development
|
|
136
116
|
prerelease: false
|
|
137
117
|
version_requirements: !ruby/object:Gem::Requirement
|
|
138
118
|
requirements:
|
|
139
119
|
- - "~>"
|
|
140
120
|
- !ruby/object:Gem::Version
|
|
141
|
-
version: 0.
|
|
121
|
+
version: 1.0.3
|
|
142
122
|
- !ruby/object:Gem::Dependency
|
|
143
123
|
name: simplecov-cobertura
|
|
144
124
|
requirement: !ruby/object:Gem::Requirement
|
|
145
125
|
requirements:
|
|
146
126
|
- - "~>"
|
|
147
127
|
- !ruby/object:Gem::Version
|
|
148
|
-
version: '
|
|
128
|
+
version: '4.0'
|
|
149
129
|
type: :development
|
|
150
130
|
prerelease: false
|
|
151
131
|
version_requirements: !ruby/object:Gem::Requirement
|
|
152
132
|
requirements:
|
|
153
133
|
- - "~>"
|
|
154
134
|
- !ruby/object:Gem::Version
|
|
155
|
-
version: '
|
|
135
|
+
version: '4.0'
|
|
156
136
|
description: A simple and efficient Ruby client for sending SMS messages through Mitake
|
|
157
137
|
SMS API
|
|
158
138
|
email:
|
|
@@ -184,14 +164,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
184
164
|
requirements:
|
|
185
165
|
- - ">="
|
|
186
166
|
- !ruby/object:Gem::Version
|
|
187
|
-
version:
|
|
167
|
+
version: 3.3.0
|
|
188
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
169
|
requirements:
|
|
190
170
|
- - ">="
|
|
191
171
|
- !ruby/object:Gem::Version
|
|
192
172
|
version: '0'
|
|
193
173
|
requirements: []
|
|
194
|
-
rubygems_version:
|
|
174
|
+
rubygems_version: 4.0.16
|
|
195
175
|
specification_version: 4
|
|
196
176
|
summary: A Ruby client for Mitake SMS API
|
|
197
177
|
test_files: []
|