sentdm 0.32.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +1 -1
  4. data/lib/sentdm/client.rb +6 -0
  5. data/lib/sentdm/models/channel_event_payload.rb +139 -2
  6. data/lib/sentdm/models/contact_event.rb +15 -7
  7. data/lib/sentdm/models/contact_event_payload.rb +77 -16
  8. data/lib/sentdm/models/conversation_messages_list.rb +125 -4
  9. data/lib/sentdm/models/message_event_payload.rb +20 -1
  10. data/lib/sentdm/models/message_retrieve_activities_response.rb +20 -5
  11. data/lib/sentdm/models/message_retrieve_status_response.rb +133 -6
  12. data/lib/sentdm/models/message_send_params.rb +67 -3
  13. data/lib/sentdm/models/message_send_response.rb +13 -4
  14. data/lib/sentdm/models/template_body.rb +82 -1
  15. data/lib/sentdm/models/template_header.rb +91 -3
  16. data/lib/sentdm/models/template_variable.rb +15 -1
  17. data/lib/sentdm/models/webhook_list_events_response.rb +324 -9
  18. data/lib/sentdm/resources/messages.rb +33 -5
  19. data/lib/sentdm/version.rb +1 -1
  20. data/lib/sentdm.rb +1 -1
  21. data/rbi/sentdm/client.rbi +6 -0
  22. data/rbi/sentdm/models/channel_event_payload.rbi +253 -2
  23. data/rbi/sentdm/models/contact_event.rbi +28 -12
  24. data/rbi/sentdm/models/contact_event_payload.rbi +118 -28
  25. data/rbi/sentdm/models/conversation_messages_list.rbi +202 -7
  26. data/rbi/sentdm/models/message_event_payload.rbi +22 -0
  27. data/rbi/sentdm/models/message_retrieve_activities_response.rbi +23 -5
  28. data/rbi/sentdm/models/message_retrieve_status_response.rbi +214 -9
  29. data/rbi/sentdm/models/message_send_params.rbi +100 -2
  30. data/rbi/sentdm/models/message_send_response.rbi +15 -5
  31. data/rbi/sentdm/models/template_body.rbi +133 -0
  32. data/rbi/sentdm/models/template_header.rbi +143 -2
  33. data/rbi/sentdm/models/template_variable.rbi +10 -0
  34. data/rbi/sentdm/models/webhook_list_events_response.rbi +478 -12
  35. data/rbi/sentdm/resources/messages.rbi +58 -3
  36. data/sig/sentdm/models/channel_event_payload.rbs +57 -0
  37. data/sig/sentdm/models/contact_event_payload.rbs +24 -9
  38. data/sig/sentdm/models/conversation_messages_list.rbs +48 -3
  39. data/sig/sentdm/models/message_event_payload.rbs +10 -0
  40. data/sig/sentdm/models/message_retrieve_activities_response.rbs +5 -0
  41. data/sig/sentdm/models/message_retrieve_status_response.rbs +48 -3
  42. data/sig/sentdm/models/message_send_params.rbs +15 -0
  43. data/sig/sentdm/models/template_body.rbs +44 -0
  44. data/sig/sentdm/models/template_header.rbs +44 -0
  45. data/sig/sentdm/models/webhook_list_events_response.rbs +145 -0
  46. data/sig/sentdm/resources/messages.rbs +3 -0
  47. metadata +2 -2
@@ -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(buttons: nil, content: nil, footer: nil, header: nil)
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
@@ -54,6 +54,17 @@ module Sentdm
54
54
  sig { params(outbound_number: String).void }
55
55
  attr_writer :outbound_number
56
56
 
57
+ # message.scheduled only: why the message is held, either because you scheduled it
58
+ # or because the recipient is inside a protected quiet-hours window. Omitted on
59
+ # every other event.
60
+ sig { returns(T.nilable(String)) }
61
+ attr_accessor :schedule_reason
62
+
63
+ # message.scheduled only: when the held message will be released for delivery, in
64
+ # UTC (yyyy-MM-ddTHH:mm:ssZ). Omitted on every other event.
65
+ sig { returns(T.nilable(String)) }
66
+ attr_accessor :scheduled_at
67
+
57
68
  # The template the message was sent from, when it was sent from one.
58
69
  sig { returns(T.nilable(String)) }
59
70
  attr_accessor :template_id
@@ -82,6 +93,8 @@ module Sentdm
82
93
  channel: String,
83
94
  message_id: String,
84
95
  outbound_number: String,
96
+ schedule_reason: T.nilable(String),
97
+ scheduled_at: T.nilable(String),
85
98
  template_id: T.nilable(String),
86
99
  template_name: T.nilable(String),
87
100
  updated_at: String
@@ -108,6 +121,13 @@ module Sentdm
108
121
  message_id: nil,
109
122
  # The recipient's number in E.164 format.
110
123
  outbound_number: nil,
124
+ # message.scheduled only: why the message is held, either because you scheduled it
125
+ # or because the recipient is inside a protected quiet-hours window. Omitted on
126
+ # every other event.
127
+ schedule_reason: nil,
128
+ # message.scheduled only: when the held message will be released for delivery, in
129
+ # UTC (yyyy-MM-ddTHH:mm:ssZ). Omitted on every other event.
130
+ scheduled_at: nil,
111
131
  # The template the message was sent from, when it was sent from one.
112
132
  template_id: nil,
113
133
  # Name of the template the message was sent from. Omitted when the message wasn't
@@ -128,6 +148,8 @@ module Sentdm
128
148
  channel: String,
129
149
  message_id: String,
130
150
  outbound_number: String,
151
+ schedule_reason: T.nilable(String),
152
+ scheduled_at: T.nilable(String),
131
153
  template_id: T.nilable(String),
132
154
  template_name: T.nilable(String),
133
155
  updated_at: String
@@ -203,8 +203,15 @@ module Sentdm
203
203
  sig { returns(T.nilable(String)) }
204
204
  attr_accessor :price
205
205
 
206
- # Activity status. Outbound: QUEUED, PROCESSED, ROUTED, SENT, DELIVERED, READ,
207
- # FAILED. Inbound (from contact): RECEIVED (terminal).
206
+ # SCHEDULED activities only: when the held message will be released for delivery,
207
+ # in UTC. Same wire name as on the send response, the message and the webhook.
208
+ # Omitted on every other activity. A message that quiet hours moved at release has
209
+ # two SCHEDULED entries, each carrying the instant as it stood at that moment.
210
+ sig { returns(T.nilable(Time)) }
211
+ attr_accessor :scheduled_at
212
+
213
+ # Activity status. Outbound: QUEUED, PROCESSED, ROUTED, SCHEDULED, SENT,
214
+ # DELIVERED, READ, FAILED. Inbound (from contact): RECEIVED (terminal).
208
215
  sig { returns(T.nilable(String)) }
209
216
  attr_reader :status
210
217
 
@@ -218,13 +225,18 @@ module Sentdm
218
225
  sig { params(timestamp: Time).void }
219
226
  attr_writer :timestamp
220
227
 
221
- # A single message activity event for v3 API
228
+ # A single message activity event for v3 API.
229
+ #
230
+ # The activity list mixes statuses, so unlike a message it is one shape rather
231
+ # than two: a SCHEDULED entry carries scheduled_at, and every other entry has no
232
+ # such key.
222
233
  sig do
223
234
  params(
224
235
  active_contact_price: T.nilable(String),
225
236
  description: String,
226
237
  from: T.nilable(String),
227
238
  price: T.nilable(String),
239
+ scheduled_at: T.nilable(Time),
228
240
  status: String,
229
241
  timestamp: Time
230
242
  ).returns(T.attached_class)
@@ -242,8 +254,13 @@ module Sentdm
242
254
  # Channel cost for this activity (e.g., SMS/WhatsApp provider cost), formatted to
243
255
  # 4 decimal places.
244
256
  price: nil,
245
- # Activity status. Outbound: QUEUED, PROCESSED, ROUTED, SENT, DELIVERED, READ,
246
- # FAILED. Inbound (from contact): RECEIVED (terminal).
257
+ # SCHEDULED activities only: when the held message will be released for delivery,
258
+ # in UTC. Same wire name as on the send response, the message and the webhook.
259
+ # Omitted on every other activity. A message that quiet hours moved at release has
260
+ # two SCHEDULED entries, each carrying the instant as it stood at that moment.
261
+ scheduled_at: nil,
262
+ # Activity status. Outbound: QUEUED, PROCESSED, ROUTED, SCHEDULED, SENT,
263
+ # DELIVERED, READ, FAILED. Inbound (from contact): RECEIVED (terminal).
247
264
  status: nil,
248
265
  # When this activity occurred
249
266
  timestamp: nil
@@ -257,6 +274,7 @@ module Sentdm
257
274
  description: String,
258
275
  from: T.nilable(String),
259
276
  price: T.nilable(String),
277
+ scheduled_at: T.nilable(Time),
260
278
  status: String,
261
279
  timestamp: Time
262
280
  }
@@ -11,7 +11,12 @@ module Sentdm
11
11
  )
12
12
  end
13
13
 
14
- # Message response for v3 API — same shape as v2 with snake_case JSON conventions
14
+ # Message response for v3 API — same shape as v2 with snake_case JSON conventions.
15
+ #
16
+ # The shape of a message that was sent immediately: it never has a scheduled_at
17
+ # key. A message that is or was held for a later instant is a
18
+ # ScheduledMessageResponse, and the endpoint decides which of the two to answer
19
+ # with. From always returns this type.
15
20
  sig do
16
21
  returns(T.nilable(Sentdm::Models::MessageRetrieveStatusResponse::Data))
17
22
  end
@@ -61,7 +66,12 @@ module Sentdm
61
66
  ).returns(T.attached_class)
62
67
  end
63
68
  def self.new(
64
- # Message response for v3 API — same shape as v2 with snake_case JSON conventions
69
+ # Message response for v3 API — same shape as v2 with snake_case JSON conventions.
70
+ #
71
+ # The shape of a message that was sent immediately: it never has a scheduled_at
72
+ # key. A message that is or was held for a later instant is a
73
+ # ScheduledMessageResponse, and the endpoint decides which of the two to answer
74
+ # with. From always returns this type.
65
75
  data: nil,
66
76
  # Error information
67
77
  error: nil,
@@ -146,7 +156,14 @@ module Sentdm
146
156
  attr_accessor :events
147
157
 
148
158
  # Structured message body format for database storage. Preserves channel-specific
149
- # components (header, body, footer, buttons).
159
+ # components (header, header media, body, footer, buttons, MMS subject and media).
160
+ #
161
+ # Persisted as the messageBody jsonb column on Messages. Every write path goes
162
+ # through MessageUtils.MessageBodyJsonOptions, which writes nulls, so the envelope
163
+ # shape is stable regardless of channel or status. Anything that rebuilds this
164
+ # object field by field — the four IMessageBodyStrategy implementations and
165
+ # MessageUtils.BuildSegmentBody — has to carry every member, or that member is
166
+ # silently dropped on whichever path forgot it.
150
167
  sig do
151
168
  returns(
152
169
  T.nilable(
@@ -202,7 +219,12 @@ module Sentdm
202
219
  sig { returns(T.nilable(String)) }
203
220
  attr_accessor :template_name
204
221
 
205
- # Message response for v3 API — same shape as v2 with snake_case JSON conventions
222
+ # Message response for v3 API — same shape as v2 with snake_case JSON conventions.
223
+ #
224
+ # The shape of a message that was sent immediately: it never has a scheduled_at
225
+ # key. A message that is or was held for a later instant is a
226
+ # ScheduledMessageResponse, and the endpoint decides which of the two to answer
227
+ # with. From always returns this type.
206
228
  sig do
207
229
  params(
208
230
  id: String,
@@ -242,7 +264,14 @@ module Sentdm
242
264
  direction: nil,
243
265
  events: nil,
244
266
  # Structured message body format for database storage. Preserves channel-specific
245
- # components (header, body, footer, buttons).
267
+ # components (header, header media, body, footer, buttons, MMS subject and media).
268
+ #
269
+ # Persisted as the messageBody jsonb column on Messages. Every write path goes
270
+ # through MessageUtils.MessageBodyJsonOptions, which writes nulls, so the envelope
271
+ # shape is stable regardless of channel or status. Anything that rebuilds this
272
+ # object field by field — the four IMessageBodyStrategy implementations and
273
+ # MessageUtils.BuildSegmentBody — has to carry every member, or that member is
274
+ # silently dropped on whichever path forgot it.
246
275
  message_body: nil,
247
276
  phone: nil,
248
277
  phone_international: nil,
@@ -363,8 +392,57 @@ module Sentdm
363
392
  sig { returns(T.nilable(String)) }
364
393
  attr_accessor :header
365
394
 
395
+ # The media asset that rode a message's header, recorded as sent.
396
+ sig do
397
+ returns(
398
+ T.nilable(
399
+ Sentdm::Models::MessageRetrieveStatusResponse::Data::MessageBody::HeaderMedia
400
+ )
401
+ )
402
+ end
403
+ attr_reader :header_media
404
+
405
+ sig do
406
+ params(
407
+ header_media:
408
+ T.nilable(
409
+ Sentdm::Models::MessageRetrieveStatusResponse::Data::MessageBody::HeaderMedia::OrHash
410
+ )
411
+ ).void
412
+ end
413
+ attr_writer :header_media
414
+
415
+ # MMS attachments, as the publicly fetchable URLs handed to the carrier. Null on
416
+ # every other channel.
417
+ #
418
+ # Persisted rather than derived because a resend and a curfew release rebuild the
419
+ # send from the stored row — MessageReplayCommandBuilder reads templateId and
420
+ # templateVariables and nothing else — so media that lives only on the original
421
+ # request would silently turn a replayed MMS into a text message.
422
+ sig do
423
+ returns(
424
+ T.nilable(
425
+ T::Array[
426
+ Sentdm::Models::MessageRetrieveStatusResponse::Data::MessageBody::Media
427
+ ]
428
+ )
429
+ )
430
+ end
431
+ attr_accessor :media
432
+
433
+ # MMS subject line. Null on every other channel.
434
+ sig { returns(T.nilable(String)) }
435
+ attr_accessor :subject
436
+
366
437
  # Structured message body format for database storage. Preserves channel-specific
367
- # components (header, body, footer, buttons).
438
+ # components (header, header media, body, footer, buttons, MMS subject and media).
439
+ #
440
+ # Persisted as the messageBody jsonb column on Messages. Every write path goes
441
+ # through MessageUtils.MessageBodyJsonOptions, which writes nulls, so the envelope
442
+ # shape is stable regardless of channel or status. Anything that rebuilds this
443
+ # object field by field — the four IMessageBodyStrategy implementations and
444
+ # MessageUtils.BuildSegmentBody — has to carry every member, or that member is
445
+ # silently dropped on whichever path forgot it.
368
446
  sig do
369
447
  params(
370
448
  buttons:
@@ -375,10 +453,38 @@ module Sentdm
375
453
  ),
376
454
  content: String,
377
455
  footer: T.nilable(String),
378
- header: T.nilable(String)
456
+ header: T.nilable(String),
457
+ header_media:
458
+ T.nilable(
459
+ Sentdm::Models::MessageRetrieveStatusResponse::Data::MessageBody::HeaderMedia::OrHash
460
+ ),
461
+ media:
462
+ T.nilable(
463
+ T::Array[
464
+ Sentdm::Models::MessageRetrieveStatusResponse::Data::MessageBody::Media::OrHash
465
+ ]
466
+ ),
467
+ subject: T.nilable(String)
379
468
  ).returns(T.attached_class)
380
469
  end
381
- def self.new(buttons: nil, content: nil, footer: nil, header: nil)
470
+ def self.new(
471
+ buttons: nil,
472
+ content: nil,
473
+ footer: nil,
474
+ header: nil,
475
+ # The media asset that rode a message's header, recorded as sent.
476
+ header_media: nil,
477
+ # MMS attachments, as the publicly fetchable URLs handed to the carrier. Null on
478
+ # every other channel.
479
+ #
480
+ # Persisted rather than derived because a resend and a curfew release rebuild the
481
+ # send from the stored row — MessageReplayCommandBuilder reads templateId and
482
+ # templateVariables and nothing else — so media that lives only on the original
483
+ # request would silently turn a replayed MMS into a text message.
484
+ media: nil,
485
+ # MMS subject line. Null on every other channel.
486
+ subject: nil
487
+ )
382
488
  end
383
489
 
384
490
  sig do
@@ -392,7 +498,18 @@ module Sentdm
392
498
  ),
393
499
  content: String,
394
500
  footer: T.nilable(String),
395
- header: T.nilable(String)
501
+ header: T.nilable(String),
502
+ header_media:
503
+ T.nilable(
504
+ Sentdm::Models::MessageRetrieveStatusResponse::Data::MessageBody::HeaderMedia
505
+ ),
506
+ media:
507
+ T.nilable(
508
+ T::Array[
509
+ Sentdm::Models::MessageRetrieveStatusResponse::Data::MessageBody::Media
510
+ ]
511
+ ),
512
+ subject: T.nilable(String)
396
513
  }
397
514
  )
398
515
  end
@@ -450,6 +567,94 @@ module Sentdm
450
567
  def to_hash
451
568
  end
452
569
  end
570
+
571
+ class HeaderMedia < Sentdm::Internal::Type::BaseModel
572
+ OrHash =
573
+ T.type_alias do
574
+ T.any(
575
+ Sentdm::Models::MessageRetrieveStatusResponse::Data::MessageBody::HeaderMedia,
576
+ Sentdm::Internal::AnyHash
577
+ )
578
+ end
579
+
580
+ # "image", "video" or "document" — taken from the header's media variable.
581
+ sig { returns(T.nilable(String)) }
582
+ attr_reader :type
583
+
584
+ sig { params(type: String).void }
585
+ attr_writer :type
586
+
587
+ # The https URL the caller supplied for this send. Never the template's stored
588
+ # props.sample, which is Meta's expiring header_handle rather than what was
589
+ # delivered.
590
+ sig { returns(T.nilable(String)) }
591
+ attr_reader :url
592
+
593
+ sig { params(url: String).void }
594
+ attr_writer :url
595
+
596
+ # The media asset that rode a message's header, recorded as sent.
597
+ sig { params(type: String, url: String).returns(T.attached_class) }
598
+ def self.new(
599
+ # "image", "video" or "document" — taken from the header's media variable.
600
+ type: nil,
601
+ # The https URL the caller supplied for this send. Never the template's stored
602
+ # props.sample, which is Meta's expiring header_handle rather than what was
603
+ # delivered.
604
+ url: nil
605
+ )
606
+ end
607
+
608
+ sig { override.returns({ type: String, url: String }) }
609
+ def to_hash
610
+ end
611
+ end
612
+
613
+ class Media < Sentdm::Internal::Type::BaseModel
614
+ OrHash =
615
+ T.type_alias do
616
+ T.any(
617
+ Sentdm::Models::MessageRetrieveStatusResponse::Data::MessageBody::Media,
618
+ Sentdm::Internal::AnyHash
619
+ )
620
+ end
621
+
622
+ # One of Constants.MmsMediaTypes when known. Advisory — the carrier reads the
623
+ # fetched object's Content-Type, not this.
624
+ sig { returns(T.nilable(String)) }
625
+ attr_accessor :media_type
626
+
627
+ sig { returns(T.nilable(String)) }
628
+ attr_reader :url
629
+
630
+ sig { params(url: String).void }
631
+ attr_writer :url
632
+
633
+ # One attachment on a message: a customer-supplied public URL handed to the
634
+ # carrier as-is.
635
+ #
636
+ # A URL and nothing else. sent.dm never takes custody of MMS media — the customer hosts it and we
637
+ # pass the link through at send time — so there is no storage key, size or expiry to record. If we ever
638
+ # do host attachments, that belongs with the change that introduces the hosting, not here.
639
+ sig do
640
+ params(media_type: T.nilable(String), url: String).returns(
641
+ T.attached_class
642
+ )
643
+ end
644
+ def self.new(
645
+ # One of Constants.MmsMediaTypes when known. Advisory — the carrier reads the
646
+ # fetched object's Content-Type, not this.
647
+ media_type: nil,
648
+ url: nil
649
+ )
650
+ end
651
+
652
+ sig do
653
+ override.returns({ media_type: T.nilable(String), url: String })
654
+ end
655
+ def to_hash
656
+ end
657
+ end
453
658
  end
454
659
  end
455
660
  end