sentdm 0.31.0 → 0.33.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 +16 -0
- data/README.md +1 -1
- data/lib/sentdm/client.rb +6 -0
- data/lib/sentdm/models/channel_event.rb +78 -0
- data/lib/sentdm/models/channel_event_payload.rb +265 -0
- data/lib/sentdm/models/contact_event.rb +77 -0
- data/lib/sentdm/models/contact_event_payload.rb +172 -0
- data/lib/sentdm/models/conversation_messages_list.rb +125 -4
- data/lib/sentdm/models/inbound_message_event.rb +16 -8
- data/lib/sentdm/models/message_event.rb +16 -8
- data/lib/sentdm/models/message_event_payload.rb +30 -1
- data/lib/sentdm/models/message_retrieve_activities_response.rb +20 -5
- data/lib/sentdm/models/message_retrieve_status_response.rb +133 -6
- data/lib/sentdm/models/message_send_params.rb +67 -3
- data/lib/sentdm/models/message_send_response.rb +13 -4
- data/lib/sentdm/models/template.rb +36 -5
- data/lib/sentdm/models/template_body.rb +102 -12
- data/lib/sentdm/models/template_body_content.rb +35 -3
- data/lib/sentdm/models/template_button.rb +8 -2
- data/lib/sentdm/models/template_button_props.rb +12 -1
- data/lib/sentdm/models/template_definition.rb +14 -2
- data/lib/sentdm/models/template_event.rb +16 -8
- data/lib/sentdm/models/template_event_payload.rb +31 -4
- data/lib/sentdm/models/template_header.rb +91 -3
- data/lib/sentdm/models/template_variable.rb +35 -4
- data/lib/sentdm/models/webhook_list_events_response.rb +332 -9
- data/lib/sentdm/models.rb +8 -0
- data/lib/sentdm/resources/messages.rb +33 -5
- data/lib/sentdm/resources/templates.rb +28 -2
- data/lib/sentdm/version.rb +1 -1
- data/lib/sentdm.rb +5 -1
- data/rbi/sentdm/client.rbi +6 -0
- data/rbi/sentdm/models/channel_event.rbi +128 -0
- data/rbi/sentdm/models/channel_event_payload.rbi +440 -0
- data/rbi/sentdm/models/contact_event.rbi +126 -0
- data/rbi/sentdm/models/contact_event_payload.rbi +254 -0
- data/rbi/sentdm/models/conversation_messages_list.rbi +202 -7
- data/rbi/sentdm/models/inbound_message_event.rbi +18 -10
- data/rbi/sentdm/models/message_event.rbi +18 -10
- data/rbi/sentdm/models/message_event_payload.rbi +34 -0
- data/rbi/sentdm/models/message_retrieve_activities_response.rbi +23 -5
- data/rbi/sentdm/models/message_retrieve_status_response.rbi +214 -9
- data/rbi/sentdm/models/message_send_params.rbi +100 -2
- data/rbi/sentdm/models/message_send_response.rbi +15 -5
- data/rbi/sentdm/models/template.rbi +58 -4
- data/rbi/sentdm/models/template_body.rbi +155 -13
- data/rbi/sentdm/models/template_body_content.rbi +59 -1
- data/rbi/sentdm/models/template_button.rbi +14 -2
- data/rbi/sentdm/models/template_button_props.rbi +22 -0
- data/rbi/sentdm/models/template_definition.rbi +20 -2
- data/rbi/sentdm/models/template_event.rbi +18 -10
- data/rbi/sentdm/models/template_event_payload.rbi +51 -8
- data/rbi/sentdm/models/template_header.rbi +143 -2
- data/rbi/sentdm/models/template_variable.rbi +38 -1
- data/rbi/sentdm/models/webhook_list_events_response.rbi +484 -12
- data/rbi/sentdm/models.rbi +8 -0
- data/rbi/sentdm/resources/messages.rbi +58 -3
- data/rbi/sentdm/resources/templates.rbi +28 -2
- data/sig/sentdm/models/channel_event.rbs +44 -0
- data/sig/sentdm/models/channel_event_payload.rbs +120 -0
- data/sig/sentdm/models/contact_event.rbs +44 -0
- data/sig/sentdm/models/contact_event_payload.rbs +78 -0
- data/sig/sentdm/models/conversation_messages_list.rbs +48 -3
- data/sig/sentdm/models/inbound_message_event.rbs +5 -0
- data/sig/sentdm/models/message_event.rbs +5 -0
- data/sig/sentdm/models/message_event_payload.rbs +15 -0
- data/sig/sentdm/models/message_retrieve_activities_response.rbs +5 -0
- data/sig/sentdm/models/message_retrieve_status_response.rbs +48 -3
- data/sig/sentdm/models/message_send_params.rbs +15 -0
- data/sig/sentdm/models/template.rbs +5 -0
- data/sig/sentdm/models/template_body.rbs +44 -0
- data/sig/sentdm/models/template_event.rbs +5 -0
- data/sig/sentdm/models/template_event_payload.rbs +9 -6
- data/sig/sentdm/models/template_header.rbs +44 -0
- data/sig/sentdm/models/webhook_list_events_response.rbs +147 -0
- data/sig/sentdm/models.rbs +8 -0
- data/sig/sentdm/resources/messages.rbs +3 -0
- metadata +14 -2
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
|
|
3
|
+
module Sentdm
|
|
4
|
+
module Models
|
|
5
|
+
class ContactEventPayload < Sentdm::Internal::Type::BaseModel
|
|
6
|
+
OrHash =
|
|
7
|
+
T.type_alias do
|
|
8
|
+
T.any(Sentdm::ContactEventPayload, Sentdm::Internal::AnyHash)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Whether the contact is opted out after this signal — the state to write to your
|
|
12
|
+
# own record. Same meaning as opt_out on the contact resource. On contact.help and
|
|
13
|
+
# contact.custom_keyword this reports the contact's existing state, which neither
|
|
14
|
+
# changes.
|
|
15
|
+
#
|
|
16
|
+
# Two signals from the same contact can arrive out of order, because each one is
|
|
17
|
+
# queued on its own rather than against the contact. Compare the envelope's
|
|
18
|
+
# timestamp before you overwrite a newer state with an older one. That timestamp
|
|
19
|
+
# is second-precision, so treat two signals stamped in the same second as
|
|
20
|
+
# unordered and read the contact resource to settle them.
|
|
21
|
+
sig { returns(T::Boolean) }
|
|
22
|
+
attr_accessor :opt_out
|
|
23
|
+
|
|
24
|
+
# How the signal reached us. INBOUND_KEYWORD means the contact sent a message
|
|
25
|
+
# whose text matched one of the keywords; PROVIDER_SIGNAL means the network
|
|
26
|
+
# reported it. A provider signal usually carries no message_id or text, so read
|
|
27
|
+
# both for null rather than inferring them from this field.
|
|
28
|
+
sig { returns(String) }
|
|
29
|
+
attr_accessor :source
|
|
30
|
+
|
|
31
|
+
# The account the contact belongs to. Present so one endpoint can serve several
|
|
32
|
+
# accounts.
|
|
33
|
+
sig { returns(T.nilable(String)) }
|
|
34
|
+
attr_reader :account_id
|
|
35
|
+
|
|
36
|
+
sig { params(account_id: String).void }
|
|
37
|
+
attr_writer :account_id
|
|
38
|
+
|
|
39
|
+
# The RCS agent the signal reached, when it reached one.
|
|
40
|
+
#
|
|
41
|
+
# Omitted entirely on channels that have no agent, rather than sent as null — an
|
|
42
|
+
# SMS or WhatsApp payload does not carry this key at all. On RCS it is the
|
|
43
|
+
# counterpart to To: a contact reaches an agent rather than a number, so exactly
|
|
44
|
+
# one of the two is populated and never both. If you run more than one agent, this
|
|
45
|
+
# is what tells you which of them the contact acted on.
|
|
46
|
+
sig { returns(T.nilable(String)) }
|
|
47
|
+
attr_accessor :agent_id
|
|
48
|
+
|
|
49
|
+
# The channel the signal arrived on, for example sms or whatsapp.
|
|
50
|
+
sig { returns(T.nilable(String)) }
|
|
51
|
+
attr_reader :channel
|
|
52
|
+
|
|
53
|
+
sig { params(channel: String).void }
|
|
54
|
+
attr_writer :channel
|
|
55
|
+
|
|
56
|
+
# The contact who raised the signal. Always populated, including for contact.help
|
|
57
|
+
# or contact.custom_keyword from a number you have not messaged before — the
|
|
58
|
+
# contact is created if it does not exist yet, so this identifier is always
|
|
59
|
+
# resolvable against the contacts API.
|
|
60
|
+
sig { returns(T.nilable(String)) }
|
|
61
|
+
attr_reader :contact_id
|
|
62
|
+
|
|
63
|
+
sig { params(contact_id: String).void }
|
|
64
|
+
attr_writer :contact_id
|
|
65
|
+
|
|
66
|
+
# The contact's number, in E.164 format with the leading + — who raised the
|
|
67
|
+
# signal. The same party message.received publishes as inbound_number.
|
|
68
|
+
sig { returns(T.nilable(String)) }
|
|
69
|
+
attr_reader :from
|
|
70
|
+
|
|
71
|
+
sig { params(from: String).void }
|
|
72
|
+
attr_writer :from
|
|
73
|
+
|
|
74
|
+
# The inbound message that carried the signal, matching message_id on the
|
|
75
|
+
# corresponding message.received event so the two can be joined.
|
|
76
|
+
#
|
|
77
|
+
# Sent as null when the signal did not arrive as a message — for example when a
|
|
78
|
+
# network processed an opt-out on your behalf — and also when the message belongs
|
|
79
|
+
# to a different account than this event, which can happen on a shared WhatsApp
|
|
80
|
+
# number. The field is always present, so read it and check for null rather than
|
|
81
|
+
# checking whether the key exists.
|
|
82
|
+
sig { returns(T.nilable(String)) }
|
|
83
|
+
attr_accessor :message_id
|
|
84
|
+
|
|
85
|
+
# The auto-reply template whose keyword the contact matched, joinable against the
|
|
86
|
+
# templates API.
|
|
87
|
+
#
|
|
88
|
+
# This is what identifies which signal arrived on contact.custom_keyword: every
|
|
89
|
+
# custom template reports the same event name, so the event alone cannot tell your
|
|
90
|
+
# booking keyword from your opening-hours one. One template holds as many keywords
|
|
91
|
+
# as you configured, so this is steadier to switch on than text.
|
|
92
|
+
#
|
|
93
|
+
# Populated on the compliance sub-types too, where it names the template that
|
|
94
|
+
# replied. Sent as null when no template was involved — a network-reported opt-out
|
|
95
|
+
# matches no keyword. The field is always present, so read it and check for null.
|
|
96
|
+
sig { returns(T.nilable(String)) }
|
|
97
|
+
attr_accessor :template_id
|
|
98
|
+
|
|
99
|
+
# The text the contact sent, for example STOP or UNSUBSCRIBE. Sent as null when
|
|
100
|
+
# the signal did not arrive as text. The field is always present, so read it and
|
|
101
|
+
# check for null rather than checking whether the key exists.
|
|
102
|
+
sig { returns(T.nilable(String)) }
|
|
103
|
+
attr_accessor :text
|
|
104
|
+
|
|
105
|
+
# The number of yours that received the signal, in E.164 format with the leading
|
|
106
|
+
# +. Tells a multi-number account which of its senders the contact acted on, which
|
|
107
|
+
# nothing else on this payload answers.
|
|
108
|
+
#
|
|
109
|
+
# This is your number, not the contact's. That is the opposite of what to means on
|
|
110
|
+
# POST /v3/messages, where it is the list of recipients you are sending to. Reply
|
|
111
|
+
# to From, not to this field, or the message goes back to yourself.
|
|
112
|
+
#
|
|
113
|
+
# Sent as null when the signal did not arrive at a number of yours — an RCS signal
|
|
114
|
+
# terminates at an agent rather than a number, and a provider-reported opt-out may
|
|
115
|
+
# name no receiving number at all. The field is always present, so read it and
|
|
116
|
+
# check for null rather than checking whether the key exists.
|
|
117
|
+
sig { returns(T.nilable(String)) }
|
|
118
|
+
attr_accessor :to
|
|
119
|
+
|
|
120
|
+
# Body of a contact.opt_in, contact.opt_out, contact.help or
|
|
121
|
+
# contact.custom_keyword event. Delivered when a contact signals a consent change,
|
|
122
|
+
# asks for help, or sends one of your own auto-reply keywords.
|
|
123
|
+
#
|
|
124
|
+
# These events state the signal outright, so you do not have to recognise keywords
|
|
125
|
+
# in the text of a message.received event. They also cover cases that produce no
|
|
126
|
+
# inbound message at all, such as a network handling an opt-out on your behalf.
|
|
127
|
+
#
|
|
128
|
+
# Two of the four change consent and two do not: contact.help and
|
|
129
|
+
# contact.custom_keyword report the state the contact already had. Read opt_out
|
|
130
|
+
# for the state and the envelope's event for what happened, rather than inferring
|
|
131
|
+
# one from the other.
|
|
132
|
+
#
|
|
133
|
+
# Fields are ordered identity → resulting state → provenance → join keys. The two
|
|
134
|
+
# parties are from and to. Note that the message family has not moved to those
|
|
135
|
+
# names yet — message.received still calls the same two parties inbound_number and
|
|
136
|
+
# outbound_number. Nothing here restates the envelope: which signal occurred is
|
|
137
|
+
# the envelope's event, and when it was emitted is its timestamp. Retries carry
|
|
138
|
+
# the same X-Webhook-Event-ID header, which is what to deduplicate on.
|
|
139
|
+
sig do
|
|
140
|
+
params(
|
|
141
|
+
opt_out: T::Boolean,
|
|
142
|
+
source: String,
|
|
143
|
+
account_id: String,
|
|
144
|
+
agent_id: T.nilable(String),
|
|
145
|
+
channel: String,
|
|
146
|
+
contact_id: String,
|
|
147
|
+
from: String,
|
|
148
|
+
message_id: T.nilable(String),
|
|
149
|
+
template_id: T.nilable(String),
|
|
150
|
+
text: T.nilable(String),
|
|
151
|
+
to: T.nilable(String)
|
|
152
|
+
).returns(T.attached_class)
|
|
153
|
+
end
|
|
154
|
+
def self.new(
|
|
155
|
+
# Whether the contact is opted out after this signal — the state to write to your
|
|
156
|
+
# own record. Same meaning as opt_out on the contact resource. On contact.help and
|
|
157
|
+
# contact.custom_keyword this reports the contact's existing state, which neither
|
|
158
|
+
# changes.
|
|
159
|
+
#
|
|
160
|
+
# Two signals from the same contact can arrive out of order, because each one is
|
|
161
|
+
# queued on its own rather than against the contact. Compare the envelope's
|
|
162
|
+
# timestamp before you overwrite a newer state with an older one. That timestamp
|
|
163
|
+
# is second-precision, so treat two signals stamped in the same second as
|
|
164
|
+
# unordered and read the contact resource to settle them.
|
|
165
|
+
opt_out:,
|
|
166
|
+
# How the signal reached us. INBOUND_KEYWORD means the contact sent a message
|
|
167
|
+
# whose text matched one of the keywords; PROVIDER_SIGNAL means the network
|
|
168
|
+
# reported it. A provider signal usually carries no message_id or text, so read
|
|
169
|
+
# both for null rather than inferring them from this field.
|
|
170
|
+
source:,
|
|
171
|
+
# The account the contact belongs to. Present so one endpoint can serve several
|
|
172
|
+
# accounts.
|
|
173
|
+
account_id: nil,
|
|
174
|
+
# The RCS agent the signal reached, when it reached one.
|
|
175
|
+
#
|
|
176
|
+
# Omitted entirely on channels that have no agent, rather than sent as null — an
|
|
177
|
+
# SMS or WhatsApp payload does not carry this key at all. On RCS it is the
|
|
178
|
+
# counterpart to To: a contact reaches an agent rather than a number, so exactly
|
|
179
|
+
# one of the two is populated and never both. If you run more than one agent, this
|
|
180
|
+
# is what tells you which of them the contact acted on.
|
|
181
|
+
agent_id: nil,
|
|
182
|
+
# The channel the signal arrived on, for example sms or whatsapp.
|
|
183
|
+
channel: nil,
|
|
184
|
+
# The contact who raised the signal. Always populated, including for contact.help
|
|
185
|
+
# or contact.custom_keyword from a number you have not messaged before — the
|
|
186
|
+
# contact is created if it does not exist yet, so this identifier is always
|
|
187
|
+
# resolvable against the contacts API.
|
|
188
|
+
contact_id: nil,
|
|
189
|
+
# The contact's number, in E.164 format with the leading + — who raised the
|
|
190
|
+
# signal. The same party message.received publishes as inbound_number.
|
|
191
|
+
from: nil,
|
|
192
|
+
# The inbound message that carried the signal, matching message_id on the
|
|
193
|
+
# corresponding message.received event so the two can be joined.
|
|
194
|
+
#
|
|
195
|
+
# Sent as null when the signal did not arrive as a message — for example when a
|
|
196
|
+
# network processed an opt-out on your behalf — and also when the message belongs
|
|
197
|
+
# to a different account than this event, which can happen on a shared WhatsApp
|
|
198
|
+
# number. The field is always present, so read it and check for null rather than
|
|
199
|
+
# checking whether the key exists.
|
|
200
|
+
message_id: nil,
|
|
201
|
+
# The auto-reply template whose keyword the contact matched, joinable against the
|
|
202
|
+
# templates API.
|
|
203
|
+
#
|
|
204
|
+
# This is what identifies which signal arrived on contact.custom_keyword: every
|
|
205
|
+
# custom template reports the same event name, so the event alone cannot tell your
|
|
206
|
+
# booking keyword from your opening-hours one. One template holds as many keywords
|
|
207
|
+
# as you configured, so this is steadier to switch on than text.
|
|
208
|
+
#
|
|
209
|
+
# Populated on the compliance sub-types too, where it names the template that
|
|
210
|
+
# replied. Sent as null when no template was involved — a network-reported opt-out
|
|
211
|
+
# matches no keyword. The field is always present, so read it and check for null.
|
|
212
|
+
template_id: nil,
|
|
213
|
+
# The text the contact sent, for example STOP or UNSUBSCRIBE. Sent as null when
|
|
214
|
+
# the signal did not arrive as text. The field is always present, so read it and
|
|
215
|
+
# check for null rather than checking whether the key exists.
|
|
216
|
+
text: nil,
|
|
217
|
+
# The number of yours that received the signal, in E.164 format with the leading
|
|
218
|
+
# +. Tells a multi-number account which of its senders the contact acted on, which
|
|
219
|
+
# nothing else on this payload answers.
|
|
220
|
+
#
|
|
221
|
+
# This is your number, not the contact's. That is the opposite of what to means on
|
|
222
|
+
# POST /v3/messages, where it is the list of recipients you are sending to. Reply
|
|
223
|
+
# to From, not to this field, or the message goes back to yourself.
|
|
224
|
+
#
|
|
225
|
+
# Sent as null when the signal did not arrive at a number of yours — an RCS signal
|
|
226
|
+
# terminates at an agent rather than a number, and a provider-reported opt-out may
|
|
227
|
+
# name no receiving number at all. The field is always present, so read it and
|
|
228
|
+
# check for null rather than checking whether the key exists.
|
|
229
|
+
to: nil
|
|
230
|
+
)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
sig do
|
|
234
|
+
override.returns(
|
|
235
|
+
{
|
|
236
|
+
opt_out: T::Boolean,
|
|
237
|
+
source: String,
|
|
238
|
+
account_id: String,
|
|
239
|
+
agent_id: T.nilable(String),
|
|
240
|
+
channel: String,
|
|
241
|
+
contact_id: String,
|
|
242
|
+
from: String,
|
|
243
|
+
message_id: T.nilable(String),
|
|
244
|
+
template_id: T.nilable(String),
|
|
245
|
+
text: T.nilable(String),
|
|
246
|
+
to: T.nilable(String)
|
|
247
|
+
}
|
|
248
|
+
)
|
|
249
|
+
end
|
|
250
|
+
def to_hash
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
@@ -112,7 +112,14 @@ module Sentdm
|
|
|
112
112
|
attr_accessor :events
|
|
113
113
|
|
|
114
114
|
# Structured message body format for database storage. Preserves channel-specific
|
|
115
|
-
# components (header, body, footer, buttons).
|
|
115
|
+
# components (header, header media, body, footer, buttons, MMS subject and media).
|
|
116
|
+
#
|
|
117
|
+
# Persisted as the messageBody jsonb column on Messages. Every write path goes
|
|
118
|
+
# through MessageUtils.MessageBodyJsonOptions, which writes nulls, so the envelope
|
|
119
|
+
# shape is stable regardless of channel or status. Anything that rebuilds this
|
|
120
|
+
# object field by field — the four IMessageBodyStrategy implementations and
|
|
121
|
+
# MessageUtils.BuildSegmentBody — has to carry every member, or that member is
|
|
122
|
+
# silently dropped on whichever path forgot it.
|
|
116
123
|
sig do
|
|
117
124
|
returns(
|
|
118
125
|
T.nilable(Sentdm::ConversationMessagesList::Message::MessageBody)
|
|
@@ -166,7 +173,12 @@ module Sentdm
|
|
|
166
173
|
sig { returns(T.nilable(String)) }
|
|
167
174
|
attr_accessor :template_name
|
|
168
175
|
|
|
169
|
-
# Message response for v3 API — same shape as v2 with snake_case JSON conventions
|
|
176
|
+
# Message response for v3 API — same shape as v2 with snake_case JSON conventions.
|
|
177
|
+
#
|
|
178
|
+
# The shape of a message that was sent immediately: it never has a scheduled_at
|
|
179
|
+
# key. A message that is or was held for a later instant is a
|
|
180
|
+
# ScheduledMessageResponse, and the endpoint decides which of the two to answer
|
|
181
|
+
# with. From always returns this type.
|
|
170
182
|
sig do
|
|
171
183
|
params(
|
|
172
184
|
id: String,
|
|
@@ -206,7 +218,14 @@ module Sentdm
|
|
|
206
218
|
direction: nil,
|
|
207
219
|
events: nil,
|
|
208
220
|
# Structured message body format for database storage. Preserves channel-specific
|
|
209
|
-
# components (header, body, footer, buttons).
|
|
221
|
+
# components (header, header media, body, footer, buttons, MMS subject and media).
|
|
222
|
+
#
|
|
223
|
+
# Persisted as the messageBody jsonb column on Messages. Every write path goes
|
|
224
|
+
# through MessageUtils.MessageBodyJsonOptions, which writes nulls, so the envelope
|
|
225
|
+
# shape is stable regardless of channel or status. Anything that rebuilds this
|
|
226
|
+
# object field by field — the four IMessageBodyStrategy implementations and
|
|
227
|
+
# MessageUtils.BuildSegmentBody — has to carry every member, or that member is
|
|
228
|
+
# silently dropped on whichever path forgot it.
|
|
210
229
|
message_body: nil,
|
|
211
230
|
phone: nil,
|
|
212
231
|
phone_international: nil,
|
|
@@ -325,8 +344,57 @@ module Sentdm
|
|
|
325
344
|
sig { returns(T.nilable(String)) }
|
|
326
345
|
attr_accessor :header
|
|
327
346
|
|
|
347
|
+
# The media asset that rode a message's header, recorded as sent.
|
|
348
|
+
sig do
|
|
349
|
+
returns(
|
|
350
|
+
T.nilable(
|
|
351
|
+
Sentdm::ConversationMessagesList::Message::MessageBody::HeaderMedia
|
|
352
|
+
)
|
|
353
|
+
)
|
|
354
|
+
end
|
|
355
|
+
attr_reader :header_media
|
|
356
|
+
|
|
357
|
+
sig do
|
|
358
|
+
params(
|
|
359
|
+
header_media:
|
|
360
|
+
T.nilable(
|
|
361
|
+
Sentdm::ConversationMessagesList::Message::MessageBody::HeaderMedia::OrHash
|
|
362
|
+
)
|
|
363
|
+
).void
|
|
364
|
+
end
|
|
365
|
+
attr_writer :header_media
|
|
366
|
+
|
|
367
|
+
# MMS attachments, as the publicly fetchable URLs handed to the carrier. Null on
|
|
368
|
+
# every other channel.
|
|
369
|
+
#
|
|
370
|
+
# Persisted rather than derived because a resend and a curfew release rebuild the
|
|
371
|
+
# send from the stored row — MessageReplayCommandBuilder reads templateId and
|
|
372
|
+
# templateVariables and nothing else — so media that lives only on the original
|
|
373
|
+
# request would silently turn a replayed MMS into a text message.
|
|
374
|
+
sig do
|
|
375
|
+
returns(
|
|
376
|
+
T.nilable(
|
|
377
|
+
T::Array[
|
|
378
|
+
Sentdm::ConversationMessagesList::Message::MessageBody::Media
|
|
379
|
+
]
|
|
380
|
+
)
|
|
381
|
+
)
|
|
382
|
+
end
|
|
383
|
+
attr_accessor :media
|
|
384
|
+
|
|
385
|
+
# MMS subject line. Null on every other channel.
|
|
386
|
+
sig { returns(T.nilable(String)) }
|
|
387
|
+
attr_accessor :subject
|
|
388
|
+
|
|
328
389
|
# Structured message body format for database storage. Preserves channel-specific
|
|
329
|
-
# components (header, body, footer, buttons).
|
|
390
|
+
# components (header, header media, body, footer, buttons, MMS subject and media).
|
|
391
|
+
#
|
|
392
|
+
# Persisted as the messageBody jsonb column on Messages. Every write path goes
|
|
393
|
+
# through MessageUtils.MessageBodyJsonOptions, which writes nulls, so the envelope
|
|
394
|
+
# shape is stable regardless of channel or status. Anything that rebuilds this
|
|
395
|
+
# object field by field — the four IMessageBodyStrategy implementations and
|
|
396
|
+
# MessageUtils.BuildSegmentBody — has to carry every member, or that member is
|
|
397
|
+
# silently dropped on whichever path forgot it.
|
|
330
398
|
sig do
|
|
331
399
|
params(
|
|
332
400
|
buttons:
|
|
@@ -337,10 +405,38 @@ module Sentdm
|
|
|
337
405
|
),
|
|
338
406
|
content: String,
|
|
339
407
|
footer: T.nilable(String),
|
|
340
|
-
header: T.nilable(String)
|
|
408
|
+
header: T.nilable(String),
|
|
409
|
+
header_media:
|
|
410
|
+
T.nilable(
|
|
411
|
+
Sentdm::ConversationMessagesList::Message::MessageBody::HeaderMedia::OrHash
|
|
412
|
+
),
|
|
413
|
+
media:
|
|
414
|
+
T.nilable(
|
|
415
|
+
T::Array[
|
|
416
|
+
Sentdm::ConversationMessagesList::Message::MessageBody::Media::OrHash
|
|
417
|
+
]
|
|
418
|
+
),
|
|
419
|
+
subject: T.nilable(String)
|
|
341
420
|
).returns(T.attached_class)
|
|
342
421
|
end
|
|
343
|
-
def self.new(
|
|
422
|
+
def self.new(
|
|
423
|
+
buttons: nil,
|
|
424
|
+
content: nil,
|
|
425
|
+
footer: nil,
|
|
426
|
+
header: nil,
|
|
427
|
+
# The media asset that rode a message's header, recorded as sent.
|
|
428
|
+
header_media: nil,
|
|
429
|
+
# MMS attachments, as the publicly fetchable URLs handed to the carrier. Null on
|
|
430
|
+
# every other channel.
|
|
431
|
+
#
|
|
432
|
+
# Persisted rather than derived because a resend and a curfew release rebuild the
|
|
433
|
+
# send from the stored row — MessageReplayCommandBuilder reads templateId and
|
|
434
|
+
# templateVariables and nothing else — so media that lives only on the original
|
|
435
|
+
# request would silently turn a replayed MMS into a text message.
|
|
436
|
+
media: nil,
|
|
437
|
+
# MMS subject line. Null on every other channel.
|
|
438
|
+
subject: nil
|
|
439
|
+
)
|
|
344
440
|
end
|
|
345
441
|
|
|
346
442
|
sig do
|
|
@@ -354,7 +450,18 @@ module Sentdm
|
|
|
354
450
|
),
|
|
355
451
|
content: String,
|
|
356
452
|
footer: T.nilable(String),
|
|
357
|
-
header: T.nilable(String)
|
|
453
|
+
header: T.nilable(String),
|
|
454
|
+
header_media:
|
|
455
|
+
T.nilable(
|
|
456
|
+
Sentdm::ConversationMessagesList::Message::MessageBody::HeaderMedia
|
|
457
|
+
),
|
|
458
|
+
media:
|
|
459
|
+
T.nilable(
|
|
460
|
+
T::Array[
|
|
461
|
+
Sentdm::ConversationMessagesList::Message::MessageBody::Media
|
|
462
|
+
]
|
|
463
|
+
),
|
|
464
|
+
subject: T.nilable(String)
|
|
358
465
|
}
|
|
359
466
|
)
|
|
360
467
|
end
|
|
@@ -412,6 +519,94 @@ module Sentdm
|
|
|
412
519
|
def to_hash
|
|
413
520
|
end
|
|
414
521
|
end
|
|
522
|
+
|
|
523
|
+
class HeaderMedia < Sentdm::Internal::Type::BaseModel
|
|
524
|
+
OrHash =
|
|
525
|
+
T.type_alias do
|
|
526
|
+
T.any(
|
|
527
|
+
Sentdm::ConversationMessagesList::Message::MessageBody::HeaderMedia,
|
|
528
|
+
Sentdm::Internal::AnyHash
|
|
529
|
+
)
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# "image", "video" or "document" — taken from the header's media variable.
|
|
533
|
+
sig { returns(T.nilable(String)) }
|
|
534
|
+
attr_reader :type
|
|
535
|
+
|
|
536
|
+
sig { params(type: String).void }
|
|
537
|
+
attr_writer :type
|
|
538
|
+
|
|
539
|
+
# The https URL the caller supplied for this send. Never the template's stored
|
|
540
|
+
# props.sample, which is Meta's expiring header_handle rather than what was
|
|
541
|
+
# delivered.
|
|
542
|
+
sig { returns(T.nilable(String)) }
|
|
543
|
+
attr_reader :url
|
|
544
|
+
|
|
545
|
+
sig { params(url: String).void }
|
|
546
|
+
attr_writer :url
|
|
547
|
+
|
|
548
|
+
# The media asset that rode a message's header, recorded as sent.
|
|
549
|
+
sig { params(type: String, url: String).returns(T.attached_class) }
|
|
550
|
+
def self.new(
|
|
551
|
+
# "image", "video" or "document" — taken from the header's media variable.
|
|
552
|
+
type: nil,
|
|
553
|
+
# The https URL the caller supplied for this send. Never the template's stored
|
|
554
|
+
# props.sample, which is Meta's expiring header_handle rather than what was
|
|
555
|
+
# delivered.
|
|
556
|
+
url: nil
|
|
557
|
+
)
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
sig { override.returns({ type: String, url: String }) }
|
|
561
|
+
def to_hash
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
class Media < Sentdm::Internal::Type::BaseModel
|
|
566
|
+
OrHash =
|
|
567
|
+
T.type_alias do
|
|
568
|
+
T.any(
|
|
569
|
+
Sentdm::ConversationMessagesList::Message::MessageBody::Media,
|
|
570
|
+
Sentdm::Internal::AnyHash
|
|
571
|
+
)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# One of Constants.MmsMediaTypes when known. Advisory — the carrier reads the
|
|
575
|
+
# fetched object's Content-Type, not this.
|
|
576
|
+
sig { returns(T.nilable(String)) }
|
|
577
|
+
attr_accessor :media_type
|
|
578
|
+
|
|
579
|
+
sig { returns(T.nilable(String)) }
|
|
580
|
+
attr_reader :url
|
|
581
|
+
|
|
582
|
+
sig { params(url: String).void }
|
|
583
|
+
attr_writer :url
|
|
584
|
+
|
|
585
|
+
# One attachment on a message: a customer-supplied public URL handed to the
|
|
586
|
+
# carrier as-is.
|
|
587
|
+
#
|
|
588
|
+
# A URL and nothing else. sent.dm never takes custody of MMS media — the customer hosts it and we
|
|
589
|
+
# pass the link through at send time — so there is no storage key, size or expiry to record. If we ever
|
|
590
|
+
# do host attachments, that belongs with the change that introduces the hosting, not here.
|
|
591
|
+
sig do
|
|
592
|
+
params(media_type: T.nilable(String), url: String).returns(
|
|
593
|
+
T.attached_class
|
|
594
|
+
)
|
|
595
|
+
end
|
|
596
|
+
def self.new(
|
|
597
|
+
# One of Constants.MmsMediaTypes when known. Advisory — the carrier reads the
|
|
598
|
+
# fetched object's Content-Type, not this.
|
|
599
|
+
media_type: nil,
|
|
600
|
+
url: nil
|
|
601
|
+
)
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
sig do
|
|
605
|
+
override.returns({ media_type: T.nilable(String), url: String })
|
|
606
|
+
end
|
|
607
|
+
def to_hash
|
|
608
|
+
end
|
|
609
|
+
end
|
|
415
610
|
end
|
|
416
611
|
end
|
|
417
612
|
end
|
|
@@ -8,14 +8,14 @@ module Sentdm
|
|
|
8
8
|
T.any(Sentdm::InboundMessageEvent, Sentdm::Internal::AnyHash)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# The specific event within the family, for example message.delivered
|
|
12
|
-
# message.received. Absent on events that have no subtype, so
|
|
13
|
-
# optional.
|
|
11
|
+
# The specific event within the family, for example message.delivered,
|
|
12
|
+
# message.received or contact.opt_out. Absent on events that have no subtype, so
|
|
13
|
+
# treat it as optional.
|
|
14
14
|
sig { returns(T.nilable(String)) }
|
|
15
15
|
attr_accessor :event
|
|
16
16
|
|
|
17
|
-
# The event family, for example message or
|
|
18
|
-
# event for the specific change.
|
|
17
|
+
# The event family, for example message, templates or contact. Route on this
|
|
18
|
+
# first, then on event for the specific change.
|
|
19
19
|
sig { returns(T.nilable(String)) }
|
|
20
20
|
attr_reader :field
|
|
21
21
|
|
|
@@ -34,6 +34,10 @@ module Sentdm
|
|
|
34
34
|
end
|
|
35
35
|
attr_writer :payload
|
|
36
36
|
|
|
37
|
+
# The event-specific body.
|
|
38
|
+
sig { returns(T.nilable(String)) }
|
|
39
|
+
attr_accessor :request_id
|
|
40
|
+
|
|
37
41
|
# When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
|
|
38
42
|
# time, not the time the underlying change happened. Use the timestamp inside the
|
|
39
43
|
# payload for the latter.
|
|
@@ -50,20 +54,23 @@ module Sentdm
|
|
|
50
54
|
event: T.nilable(String),
|
|
51
55
|
field: String,
|
|
52
56
|
payload: T.nilable(Sentdm::InboundMessageEventPayload::OrHash),
|
|
57
|
+
request_id: T.nilable(String),
|
|
53
58
|
timestamp: String
|
|
54
59
|
).returns(T.attached_class)
|
|
55
60
|
end
|
|
56
61
|
def self.new(
|
|
57
|
-
# The specific event within the family, for example message.delivered
|
|
58
|
-
# message.received. Absent on events that have no subtype, so
|
|
59
|
-
# optional.
|
|
62
|
+
# The specific event within the family, for example message.delivered,
|
|
63
|
+
# message.received or contact.opt_out. Absent on events that have no subtype, so
|
|
64
|
+
# treat it as optional.
|
|
60
65
|
event: nil,
|
|
61
|
-
# The event family, for example message or
|
|
62
|
-
# event for the specific change.
|
|
66
|
+
# The event family, for example message, templates or contact. Route on this
|
|
67
|
+
# first, then on event for the specific change.
|
|
63
68
|
field: nil,
|
|
64
69
|
# Body of a message.received event. Delivered when a contact messages one of your
|
|
65
70
|
# numbers.
|
|
66
71
|
payload: nil,
|
|
72
|
+
# The event-specific body.
|
|
73
|
+
request_id: nil,
|
|
67
74
|
# When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
|
|
68
75
|
# time, not the time the underlying change happened. Use the timestamp inside the
|
|
69
76
|
# payload for the latter.
|
|
@@ -77,6 +84,7 @@ module Sentdm
|
|
|
77
84
|
event: T.nilable(String),
|
|
78
85
|
field: String,
|
|
79
86
|
payload: T.nilable(Sentdm::InboundMessageEventPayload),
|
|
87
|
+
request_id: T.nilable(String),
|
|
80
88
|
timestamp: String
|
|
81
89
|
}
|
|
82
90
|
)
|
|
@@ -6,14 +6,14 @@ module Sentdm
|
|
|
6
6
|
OrHash =
|
|
7
7
|
T.type_alias { T.any(Sentdm::MessageEvent, Sentdm::Internal::AnyHash) }
|
|
8
8
|
|
|
9
|
-
# The specific event within the family, for example message.delivered
|
|
10
|
-
# message.received. Absent on events that have no subtype, so
|
|
11
|
-
# optional.
|
|
9
|
+
# The specific event within the family, for example message.delivered,
|
|
10
|
+
# message.received or contact.opt_out. Absent on events that have no subtype, so
|
|
11
|
+
# treat it as optional.
|
|
12
12
|
sig { returns(T.nilable(String)) }
|
|
13
13
|
attr_accessor :event
|
|
14
14
|
|
|
15
|
-
# The event family, for example message or
|
|
16
|
-
# event for the specific change.
|
|
15
|
+
# The event family, for example message, templates or contact. Route on this
|
|
16
|
+
# first, then on event for the specific change.
|
|
17
17
|
sig { returns(T.nilable(String)) }
|
|
18
18
|
attr_reader :field
|
|
19
19
|
|
|
@@ -31,6 +31,10 @@ module Sentdm
|
|
|
31
31
|
end
|
|
32
32
|
attr_writer :payload
|
|
33
33
|
|
|
34
|
+
# The event-specific body.
|
|
35
|
+
sig { returns(T.nilable(String)) }
|
|
36
|
+
attr_accessor :request_id
|
|
37
|
+
|
|
34
38
|
# When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
|
|
35
39
|
# time, not the time the underlying change happened. Use the timestamp inside the
|
|
36
40
|
# payload for the latter.
|
|
@@ -47,21 +51,24 @@ module Sentdm
|
|
|
47
51
|
event: T.nilable(String),
|
|
48
52
|
field: String,
|
|
49
53
|
payload: T.nilable(Sentdm::MessageEventPayload::OrHash),
|
|
54
|
+
request_id: T.nilable(String),
|
|
50
55
|
timestamp: String
|
|
51
56
|
).returns(T.attached_class)
|
|
52
57
|
end
|
|
53
58
|
def self.new(
|
|
54
|
-
# The specific event within the family, for example message.delivered
|
|
55
|
-
# message.received. Absent on events that have no subtype, so
|
|
56
|
-
# optional.
|
|
59
|
+
# The specific event within the family, for example message.delivered,
|
|
60
|
+
# message.received or contact.opt_out. Absent on events that have no subtype, so
|
|
61
|
+
# treat it as optional.
|
|
57
62
|
event: nil,
|
|
58
|
-
# The event family, for example message or
|
|
59
|
-
# event for the specific change.
|
|
63
|
+
# The event family, for example message, templates or contact. Route on this
|
|
64
|
+
# first, then on event for the specific change.
|
|
60
65
|
field: nil,
|
|
61
66
|
# Body of an outbound message lifecycle event. Delivered once per status change,
|
|
62
67
|
# so a single message produces several of these as it moves toward a terminal
|
|
63
68
|
# status.
|
|
64
69
|
payload: nil,
|
|
70
|
+
# The event-specific body.
|
|
71
|
+
request_id: nil,
|
|
65
72
|
# When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
|
|
66
73
|
# time, not the time the underlying change happened. Use the timestamp inside the
|
|
67
74
|
# payload for the latter.
|
|
@@ -75,6 +82,7 @@ module Sentdm
|
|
|
75
82
|
event: T.nilable(String),
|
|
76
83
|
field: String,
|
|
77
84
|
payload: T.nilable(Sentdm::MessageEventPayload),
|
|
85
|
+
request_id: T.nilable(String),
|
|
78
86
|
timestamp: String
|
|
79
87
|
}
|
|
80
88
|
)
|