ruby-whatsapp 0.4.2 → 0.6.1
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 +143 -0
- data/README.md +70 -7
- data/docs/README.md +4 -1
- data/docs/business_phone_number/README.md +12 -1
- data/docs/business_phone_number/contact_book.md +90 -0
- data/docs/business_phone_number/username.md +157 -0
- data/docs/configuration.md +6 -1
- data/docs/errors.md +12 -1
- data/docs/message_templates/README.md +1 -1
- data/docs/message_templates/components.md +37 -1
- data/docs/messages/README.md +86 -9
- data/docs/messages/address.md +1 -1
- data/docs/messages/audio.md +1 -1
- data/docs/messages/contacts.md +1 -1
- data/docs/messages/document.md +1 -1
- data/docs/messages/image.md +1 -1
- data/docs/messages/interactive.md +64 -3
- data/docs/messages/location.md +1 -1
- data/docs/messages/location_request.md +1 -1
- data/docs/messages/reaction.md +1 -1
- data/docs/messages/sticker.md +1 -1
- data/docs/messages/template.md +28 -1
- data/docs/messages/text.md +1 -1
- data/docs/messages/video.md +1 -1
- data/docs/schedule/README.md +183 -0
- data/docs/webhooks/README.md +20 -12
- data/docs/webhooks/business_username_updates.md +55 -0
- data/docs/webhooks/history.md +78 -15
- data/docs/webhooks/messages.md +141 -6
- data/docs/webhooks/smb_app_state_sync.md +50 -12
- data/docs/webhooks/smb_message_echoes.md +58 -21
- data/docs/webhooks/user_id_update.md +84 -0
- data/docs/webhooks/user_preferences.md +55 -16
- data/lib/ruby/whatsapp/business_phone_number/contact_book/delete.rb +87 -0
- data/lib/ruby/whatsapp/business_phone_number/contact_book/deletion.rb +50 -0
- data/lib/ruby/whatsapp/business_phone_number/contact_book.rb +25 -0
- data/lib/ruby/whatsapp/business_phone_number/transport.rb +6 -5
- data/lib/ruby/whatsapp/business_phone_number/username/claim.rb +180 -0
- data/lib/ruby/whatsapp/business_phone_number/username/delete.rb +36 -0
- data/lib/ruby/whatsapp/business_phone_number/username/details.rb +66 -0
- data/lib/ruby/whatsapp/business_phone_number/username/get.rb +33 -0
- data/lib/ruby/whatsapp/business_phone_number/username/suggest.rb +37 -0
- data/lib/ruby/whatsapp/business_phone_number/username/suggestions.rb +50 -0
- data/lib/ruby/whatsapp/business_phone_number/username.rb +27 -0
- data/lib/ruby/whatsapp/business_phone_number.rb +9 -0
- data/lib/ruby/whatsapp/message_templates/button/request_contact_info.rb +66 -0
- data/lib/ruby/whatsapp/message_templates/button.rb +5 -2
- data/lib/ruby/whatsapp/message_templates/component_set.rb +28 -0
- data/lib/ruby/whatsapp/message_templates/response/paging.rb +2 -1
- data/lib/ruby/whatsapp/messages/base.rb +47 -5
- data/lib/ruby/whatsapp/messages/interactive/request_contact_info.rb +59 -0
- data/lib/ruby/whatsapp/messages/interactive.rb +22 -4
- data/lib/ruby/whatsapp/messages/response/contacts.rb +22 -5
- data/lib/ruby/whatsapp/schedule/create.rb +101 -0
- data/lib/ruby/whatsapp/schedule/list.rb +50 -0
- data/lib/ruby/whatsapp/schedule/response/collection.rb +61 -0
- data/lib/ruby/whatsapp/schedule/response/created.rb +30 -0
- data/lib/ruby/whatsapp/schedule/response/node.rb +85 -0
- data/lib/ruby/whatsapp/schedule/response.rb +18 -0
- data/lib/ruby/whatsapp/schedule/statuses.rb +19 -0
- data/lib/ruby/whatsapp/schedule/transport.rb +24 -0
- data/lib/ruby/whatsapp/schedule.rb +15 -0
- data/lib/ruby/whatsapp/version.rb +1 -1
- data/lib/ruby/whatsapp/webhook/business_username_updates.rb +51 -0
- data/lib/ruby/whatsapp/webhook/change.rb +3 -1
- data/lib/ruby/whatsapp/webhook/contact.rb +26 -3
- data/lib/ruby/whatsapp/webhook/history/chunk.rb +50 -0
- data/lib/ruby/whatsapp/webhook/history/thread.rb +64 -0
- data/lib/ruby/whatsapp/webhook/history.rb +73 -34
- data/lib/ruby/whatsapp/webhook/message/base.rb +27 -2
- data/lib/ruby/whatsapp/webhook/message/contacts/contact.rb +15 -2
- data/lib/ruby/whatsapp/webhook/message/context.rb +23 -2
- data/lib/ruby/whatsapp/webhook/message/edit.rb +50 -0
- data/lib/ruby/whatsapp/webhook/message/revoke.rb +36 -0
- data/lib/ruby/whatsapp/webhook/message/system.rb +19 -2
- data/lib/ruby/whatsapp/webhook/message/unknown.rb +2 -2
- data/lib/ruby/whatsapp/webhook/message.rb +3 -0
- data/lib/ruby/whatsapp/webhook/messages.rb +4 -3
- data/lib/ruby/whatsapp/webhook/smb_app_state_sync/contact.rb +76 -0
- data/lib/ruby/whatsapp/webhook/smb_app_state_sync/state_sync.rb +18 -4
- data/lib/ruby/whatsapp/webhook/smb_app_state_sync.rb +33 -9
- data/lib/ruby/whatsapp/webhook/smb_message_echoes/echo.rb +60 -0
- data/lib/ruby/whatsapp/webhook/smb_message_echoes.rb +26 -11
- data/lib/ruby/whatsapp/webhook/status.rb +23 -2
- data/lib/ruby/whatsapp/webhook/user_id_update/entry.rb +56 -0
- data/lib/ruby/whatsapp/webhook/user_id_update/id_change.rb +35 -0
- data/lib/ruby/whatsapp/webhook/user_id_update.rb +61 -0
- data/lib/ruby/whatsapp/webhook/user_preferences/entry.rb +76 -0
- data/lib/ruby/whatsapp/webhook/user_preferences.rb +44 -23
- metadata +38 -1
|
@@ -10,13 +10,33 @@ module Whatsapp
|
|
|
10
10
|
# @return [String, nil]
|
|
11
11
|
attr_accessor :profile_name
|
|
12
12
|
|
|
13
|
+
# @!attribute [rw] username
|
|
14
|
+
# @return [String, nil] Set only if the user has adopted a username. Read from
|
|
15
|
+
# `profile.username`, where Meta's App Dashboard test payloads put it, falling
|
|
16
|
+
# back to a flat top-level `username` — `user_id`/`parent_user_id` arrive flat
|
|
17
|
+
# on this same hash and Meta publishes no reference schema for the contact
|
|
18
|
+
# object, so both locations are read rather than betting on one.
|
|
19
|
+
attr_accessor :username
|
|
20
|
+
|
|
13
21
|
# @!attribute [rw] wa_id
|
|
14
|
-
# @return [String, nil]
|
|
22
|
+
# @return [String, nil] Omitted once the user adopts a username, unless a
|
|
23
|
+
# recent interaction or the contact book still allows sharing the phone number.
|
|
15
24
|
attr_accessor :wa_id
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
# @!attribute [rw] user_id
|
|
27
|
+
# @return [String, nil] The user's business-scoped user ID (BSUID).
|
|
28
|
+
attr_accessor :user_id
|
|
29
|
+
|
|
30
|
+
# @!attribute [rw] parent_user_id
|
|
31
|
+
# @return [String, nil] The user's parent BSUID, present only when enrolled.
|
|
32
|
+
attr_accessor :parent_user_id
|
|
33
|
+
|
|
34
|
+
def initialize(profile_name: nil, username: nil, wa_id: nil, user_id: nil, parent_user_id: nil)
|
|
18
35
|
@profile_name = profile_name
|
|
36
|
+
@username = username
|
|
19
37
|
@wa_id = wa_id
|
|
38
|
+
@user_id = user_id
|
|
39
|
+
@parent_user_id = parent_user_id
|
|
20
40
|
end
|
|
21
41
|
|
|
22
42
|
class << self
|
|
@@ -27,7 +47,10 @@ module Whatsapp
|
|
|
27
47
|
|
|
28
48
|
new(
|
|
29
49
|
profile_name: data.dig("profile", "name"),
|
|
30
|
-
|
|
50
|
+
username: data.dig("profile", "username") || data["username"],
|
|
51
|
+
wa_id: data["wa_id"],
|
|
52
|
+
user_id: data["user_id"],
|
|
53
|
+
parent_user_id: data["parent_user_id"]
|
|
31
54
|
)
|
|
32
55
|
end
|
|
33
56
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class History
|
|
6
|
+
# A single `history[]` entry: one chunk of a chat-history sync, plus the counters
|
|
7
|
+
# that tell you how far through the sync it is.
|
|
8
|
+
class Chunk
|
|
9
|
+
# @!attribute [rw] phase
|
|
10
|
+
# @return [Integer, nil] Which sync phase this chunk belongs to.
|
|
11
|
+
attr_accessor :phase
|
|
12
|
+
|
|
13
|
+
# @!attribute [rw] chunk_order
|
|
14
|
+
# @return [Integer, nil] This chunk's position within its phase.
|
|
15
|
+
attr_accessor :chunk_order
|
|
16
|
+
|
|
17
|
+
# @!attribute [rw] progress
|
|
18
|
+
# @return [Integer, nil] Percent complete, 0-100.
|
|
19
|
+
attr_accessor :progress
|
|
20
|
+
|
|
21
|
+
# @!attribute [rw] threads
|
|
22
|
+
# @return [Array<Thread>]
|
|
23
|
+
attr_accessor :threads
|
|
24
|
+
|
|
25
|
+
def initialize(phase: nil, chunk_order: nil, progress: nil, threads: [])
|
|
26
|
+
@phase = phase
|
|
27
|
+
@chunk_order = chunk_order
|
|
28
|
+
@progress = progress
|
|
29
|
+
@threads = threads
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
# @param data [Hash, nil] A raw `history[]` entry.
|
|
34
|
+
# @return [Chunk]
|
|
35
|
+
def deserialize(data)
|
|
36
|
+
data ||= {}
|
|
37
|
+
metadata = data["metadata"] || {}
|
|
38
|
+
|
|
39
|
+
new(
|
|
40
|
+
phase: metadata["phase"],
|
|
41
|
+
chunk_order: metadata["chunk_order"],
|
|
42
|
+
progress: metadata["progress"],
|
|
43
|
+
threads: Array(data["threads"]).map { |thread_data| Thread.deserialize(thread_data) }
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class History
|
|
6
|
+
# One conversation within a history {Chunk}: who it is with, and the messages in it.
|
|
7
|
+
#
|
|
8
|
+
# Named for Meta's `threads[]` key. Nested under {History}, so it never shadows
|
|
9
|
+
# `::Thread` for anything outside this class.
|
|
10
|
+
class Thread
|
|
11
|
+
# @!attribute [rw] id
|
|
12
|
+
# @return [String, nil] The thread's identifier — the user's phone number, omitted
|
|
13
|
+
# if they had already adopted a username when the sync was requested and their
|
|
14
|
+
# number was no longer shareable. Use {#context} to identify them instead.
|
|
15
|
+
attr_accessor :id
|
|
16
|
+
|
|
17
|
+
# @!attribute [rw] context
|
|
18
|
+
# @return [Contact, nil] Who the thread is with (`wa_id`, `user_id`,
|
|
19
|
+
# `parent_user_id`, `username`). Deserialized with the same
|
|
20
|
+
# {Whatsapp::Webhook::Contact} as every other contact entry — its `username`
|
|
21
|
+
# read already falls back to a flat key, which is where Meta puts it here, and
|
|
22
|
+
# its `profile_name` is simply `nil` since a thread context carries no profile.
|
|
23
|
+
attr_accessor :context
|
|
24
|
+
|
|
25
|
+
# @!attribute [rw] messages
|
|
26
|
+
# @return [Array<Message::Base>] The synced messages, dispatched by `type` like
|
|
27
|
+
# any inbound message.
|
|
28
|
+
#
|
|
29
|
+
# Each one also carries a `history_context.status` (its delivery state at sync
|
|
30
|
+
# time), which is **not** modelled: it would put a history-only field on
|
|
31
|
+
# `Message::Base`, the envelope shared by every inbound message type, where it
|
|
32
|
+
# could never appear. Read it from {#raw} instead.
|
|
33
|
+
attr_accessor :messages
|
|
34
|
+
|
|
35
|
+
# @!attribute [r] raw
|
|
36
|
+
# @return [Hash] The untouched thread hash, so the unmodelled per-message
|
|
37
|
+
# `history_context` stays reachable. Read-only; not a copy.
|
|
38
|
+
attr_reader :raw
|
|
39
|
+
|
|
40
|
+
def initialize(id: nil, context: nil, messages: [], raw: {})
|
|
41
|
+
@id = id
|
|
42
|
+
@context = context
|
|
43
|
+
@messages = messages
|
|
44
|
+
@raw = raw
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class << self
|
|
48
|
+
# @param data [Hash, nil] A raw `threads[]` entry.
|
|
49
|
+
# @return [Thread]
|
|
50
|
+
def deserialize(data)
|
|
51
|
+
data ||= {}
|
|
52
|
+
|
|
53
|
+
new(
|
|
54
|
+
id: data["id"],
|
|
55
|
+
context: data["context"] ? Contact.deserialize(data["context"]) : nil,
|
|
56
|
+
messages: Array(data["messages"]).map { |message_data| Message.deserialize(message_data) },
|
|
57
|
+
raw: data
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -2,50 +2,89 @@
|
|
|
2
2
|
|
|
3
3
|
module Whatsapp
|
|
4
4
|
module Webhook
|
|
5
|
-
# Chat history synchronization for onboarded business customers
|
|
5
|
+
# Chat history synchronization for onboarded business customers — one of the fields
|
|
6
|
+
# with a confirmed, JSON-example-backed schema, alongside {Messages},
|
|
7
|
+
# {UserIdUpdate}, {BusinessUsernameUpdates}, and {UserPreferences}.
|
|
6
8
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
9
|
+
# History arrives in chunks. Each `history[]` entry is one {Chunk}, carrying its own
|
|
10
|
+
# sync `metadata` (`phase`/`chunk_order`/`progress`) and the {Thread}s in it — note
|
|
11
|
+
# that the value-level `metadata` is the ordinary business-phone-number
|
|
12
|
+
# {Whatsapp::Webhook::Metadata}, a different object entirely.
|
|
13
|
+
#
|
|
14
|
+
# Meta sends a second variant of this field for a synced media asset, which carries
|
|
15
|
+
# `contacts` and `messages` at the value level instead of inside a thread; both are
|
|
16
|
+
# mapped here, and are empty for a thread-carrying payload.
|
|
17
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids
|
|
13
18
|
class History
|
|
14
|
-
# @!attribute [rw]
|
|
15
|
-
# @return [
|
|
16
|
-
attr_accessor :
|
|
17
|
-
|
|
18
|
-
# @!attribute [rw]
|
|
19
|
-
# @return [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
# @!attribute [rw] messaging_product
|
|
20
|
+
# @return [String, nil]
|
|
21
|
+
attr_accessor :messaging_product
|
|
22
|
+
|
|
23
|
+
# @!attribute [rw] metadata
|
|
24
|
+
# @return [Metadata, nil] The business phone number this history belongs to — not
|
|
25
|
+
# the per-chunk sync metadata, which lives on {Chunk}.
|
|
26
|
+
attr_accessor :metadata
|
|
27
|
+
|
|
28
|
+
# @!attribute [rw] chunks
|
|
29
|
+
# @return [Array<Chunk>]
|
|
30
|
+
attr_accessor :chunks
|
|
31
|
+
|
|
32
|
+
# @!attribute [rw] contacts
|
|
33
|
+
# @return [Array<Contact>] Populated only by the media-asset variant.
|
|
34
|
+
attr_accessor :contacts
|
|
35
|
+
|
|
36
|
+
# @!attribute [rw] messages
|
|
37
|
+
# @return [Array<Message::Base>] Populated only by the media-asset variant; a
|
|
38
|
+
# thread-carrying payload puts its messages on {Thread#messages} instead.
|
|
39
|
+
attr_accessor :messages
|
|
40
|
+
|
|
41
|
+
# @!attribute [r] raw
|
|
42
|
+
# @return [Hash] The untouched `value` hash. A diagnostic escape hatch, not part of
|
|
43
|
+
# the supported API: this field has two payload variants and the deeper one nests
|
|
44
|
+
# three levels, so a key that drifts would leave a typed accessor empty with no
|
|
45
|
+
# other trace. Treat it as read-only — it is the object `JSON.parse` returned.
|
|
46
|
+
attr_reader :raw
|
|
47
|
+
|
|
48
|
+
def initialize(messaging_product:, metadata:, chunks:, contacts:, messages:, raw: {})
|
|
49
|
+
@messaging_product = messaging_product
|
|
50
|
+
@metadata = metadata
|
|
51
|
+
@chunks = chunks
|
|
52
|
+
@contacts = contacts
|
|
53
|
+
@messages = messages
|
|
54
|
+
@raw = raw
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# A delivery normally carries exactly one chunk, so these three read through to it
|
|
58
|
+
# rather than making every caller reach for `chunks.first`. Check {#chunks} directly
|
|
59
|
+
# if you need to handle a multi-chunk delivery.
|
|
60
|
+
# @return [Integer, nil]
|
|
61
|
+
def phase
|
|
62
|
+
chunks.first&.phase
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @return [Integer, nil]
|
|
66
|
+
def chunk_order
|
|
67
|
+
chunks.first&.chunk_order
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @return [Integer, nil] Percent complete, 0-100.
|
|
71
|
+
def progress
|
|
72
|
+
chunks.first&.progress
|
|
35
73
|
end
|
|
36
74
|
|
|
37
75
|
class << self
|
|
38
|
-
# @param data [Hash] The raw `value` hash.
|
|
76
|
+
# @param data [Hash] The raw `history` field's `value` hash.
|
|
39
77
|
# @return [History]
|
|
40
78
|
def deserialize(data)
|
|
41
79
|
data ||= {}
|
|
42
|
-
metadata = data["metadata"] || {}
|
|
43
80
|
|
|
44
81
|
new(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
82
|
+
messaging_product: data["messaging_product"],
|
|
83
|
+
metadata: data["metadata"] ? Metadata.deserialize(data["metadata"]) : nil,
|
|
84
|
+
chunks: Array(data["history"]).map { |chunk_data| Chunk.deserialize(chunk_data) },
|
|
85
|
+
contacts: Array(data["contacts"]).map { |contact_data| Contact.deserialize(contact_data) },
|
|
86
|
+
messages: Array(data["messages"]).map { |message_data| Message.deserialize(message_data) },
|
|
87
|
+
raw: data
|
|
49
88
|
)
|
|
50
89
|
end
|
|
51
90
|
end
|
|
@@ -7,9 +7,24 @@ module Whatsapp
|
|
|
7
7
|
# wamid, when it arrived, and (optionally) what it's replying to.
|
|
8
8
|
class Base
|
|
9
9
|
# @!attribute [rw] from
|
|
10
|
-
# @return [String, nil]
|
|
10
|
+
# @return [String, nil] Omitted once the sender adopts a username, unless a
|
|
11
|
+
# recent interaction or the contact book still allows sharing the phone number.
|
|
11
12
|
attr_accessor :from
|
|
12
13
|
|
|
14
|
+
# @!attribute [rw] from_user_id
|
|
15
|
+
# @return [String, nil] The sender's business-scoped user ID (BSUID).
|
|
16
|
+
attr_accessor :from_user_id
|
|
17
|
+
|
|
18
|
+
# @!attribute [rw] from_parent_user_id
|
|
19
|
+
# @return [String, nil] The sender's parent BSUID, present only when enrolled.
|
|
20
|
+
attr_accessor :from_parent_user_id
|
|
21
|
+
|
|
22
|
+
# @!attribute [rw] group_id
|
|
23
|
+
# @return [String, nil] Set only when the message was sent in a group chat.
|
|
24
|
+
# Reading groups works; this gem wraps no Groups API, so there is no way to
|
|
25
|
+
# reply into one — the id is for correlation and for filtering group traffic out.
|
|
26
|
+
attr_accessor :group_id
|
|
27
|
+
|
|
13
28
|
# @!attribute [rw] id
|
|
14
29
|
# @return [String, nil] The message's wamid.
|
|
15
30
|
attr_accessor :id
|
|
@@ -30,8 +45,15 @@ module Whatsapp
|
|
|
30
45
|
# @return [Referral, nil]
|
|
31
46
|
attr_accessor :referral
|
|
32
47
|
|
|
33
|
-
def initialize(
|
|
48
|
+
def initialize(
|
|
49
|
+
id:, timestamp:, type:,
|
|
50
|
+
from: nil, from_user_id: nil, from_parent_user_id: nil, group_id: nil,
|
|
51
|
+
context: nil, referral: nil
|
|
52
|
+
)
|
|
34
53
|
@from = from
|
|
54
|
+
@from_user_id = from_user_id
|
|
55
|
+
@from_parent_user_id = from_parent_user_id
|
|
56
|
+
@group_id = group_id
|
|
35
57
|
@id = id
|
|
36
58
|
@timestamp = timestamp
|
|
37
59
|
@type = type
|
|
@@ -47,6 +69,9 @@ module Whatsapp
|
|
|
47
69
|
def common_attributes(data)
|
|
48
70
|
{
|
|
49
71
|
from: data["from"],
|
|
72
|
+
from_user_id: data["from_user_id"],
|
|
73
|
+
from_parent_user_id: data["from_parent_user_id"],
|
|
74
|
+
group_id: data["group_id"],
|
|
50
75
|
id: data["id"],
|
|
51
76
|
timestamp: data["timestamp"],
|
|
52
77
|
type: data["type"],
|
|
@@ -34,7 +34,16 @@ module Whatsapp
|
|
|
34
34
|
# @return [String, nil] Date in YYYY-MM-DD format.
|
|
35
35
|
attr_accessor :birthday
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
# @!attribute [rw] vcard
|
|
38
|
+
# @return [String, nil] The card's raw vCard, set only when `origin` is `"other"`.
|
|
39
|
+
attr_accessor :vcard
|
|
40
|
+
|
|
41
|
+
# @!attribute [rw] origin
|
|
42
|
+
# @return [String, nil] `"contact_request"` (a `REQUEST_CONTACT_INFO` button tap)
|
|
43
|
+
# or `"other"` (shared directly from the chat).
|
|
44
|
+
attr_accessor :origin
|
|
45
|
+
|
|
46
|
+
def initialize(name:, phones:, emails:, addresses:, urls:, org: nil, birthday: nil, vcard: nil, origin: nil)
|
|
38
47
|
@name = name
|
|
39
48
|
@phones = phones
|
|
40
49
|
@emails = emails
|
|
@@ -42,6 +51,8 @@ module Whatsapp
|
|
|
42
51
|
@org = org
|
|
43
52
|
@urls = urls
|
|
44
53
|
@birthday = birthday
|
|
54
|
+
@vcard = vcard
|
|
55
|
+
@origin = origin
|
|
45
56
|
end
|
|
46
57
|
|
|
47
58
|
class << self
|
|
@@ -57,7 +68,9 @@ module Whatsapp
|
|
|
57
68
|
addresses: Array(data["addresses"]).map { |address_data| Address.deserialize(address_data) },
|
|
58
69
|
org: data["org"] ? Org.deserialize(data["org"]) : nil,
|
|
59
70
|
urls: Array(data["urls"]).map { |url_data| Url.deserialize(url_data) },
|
|
60
|
-
birthday: data["birthday"]
|
|
71
|
+
birthday: data["birthday"],
|
|
72
|
+
vcard: data["vcard"],
|
|
73
|
+
origin: data["origin"]
|
|
61
74
|
)
|
|
62
75
|
end
|
|
63
76
|
end
|
|
@@ -7,9 +7,22 @@ module Whatsapp
|
|
|
7
7
|
# swipe, or a tap on an interactive button/list from an earlier message).
|
|
8
8
|
class Context
|
|
9
9
|
# @!attribute [rw] from
|
|
10
|
-
# @return [String, nil]
|
|
10
|
+
# @return [String, nil] The WhatsApp ID of the quoted message's sender, absent
|
|
11
|
+
# once they adopt a username and their phone number is no longer shareable.
|
|
11
12
|
attr_accessor :from
|
|
12
13
|
|
|
14
|
+
# @!attribute [rw] from_user_id
|
|
15
|
+
# @return [String, nil] The quoted sender's BSUID, the fallback identifier once
|
|
16
|
+
# {#from} is gone. **The key name is inferred** by symmetry with
|
|
17
|
+
# {Base#from_user_id} — Meta has not published a `context` example carrying
|
|
18
|
+
# it. Validate against a real payload before depending on it.
|
|
19
|
+
attr_accessor :from_user_id
|
|
20
|
+
|
|
21
|
+
# @!attribute [rw] from_parent_user_id
|
|
22
|
+
# @return [String, nil] The quoted sender's parent BSUID, present only for
|
|
23
|
+
# businesses enrolled in parent BSUIDs. Key name inferred, as above.
|
|
24
|
+
attr_accessor :from_parent_user_id
|
|
25
|
+
|
|
13
26
|
# @!attribute [rw] id
|
|
14
27
|
# @return [String, nil] The wamid of the message being replied to.
|
|
15
28
|
attr_accessor :id
|
|
@@ -22,8 +35,14 @@ module Whatsapp
|
|
|
22
35
|
# @return [Boolean, nil]
|
|
23
36
|
attr_accessor :frequently_forwarded
|
|
24
37
|
|
|
25
|
-
def initialize(
|
|
38
|
+
def initialize(
|
|
39
|
+
id:,
|
|
40
|
+
from: nil, from_user_id: nil, from_parent_user_id: nil,
|
|
41
|
+
forwarded: nil, frequently_forwarded: nil
|
|
42
|
+
)
|
|
26
43
|
@from = from
|
|
44
|
+
@from_user_id = from_user_id
|
|
45
|
+
@from_parent_user_id = from_parent_user_id
|
|
27
46
|
@id = id
|
|
28
47
|
@forwarded = forwarded
|
|
29
48
|
@frequently_forwarded = frequently_forwarded
|
|
@@ -37,6 +56,8 @@ module Whatsapp
|
|
|
37
56
|
|
|
38
57
|
new(
|
|
39
58
|
from: data["from"],
|
|
59
|
+
from_user_id: data["from_user_id"],
|
|
60
|
+
from_parent_user_id: data["from_parent_user_id"],
|
|
40
61
|
id: data["id"],
|
|
41
62
|
forwarded: data["forwarded"],
|
|
42
63
|
frequently_forwarded: data["frequently_forwarded"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class Message
|
|
6
|
+
# A user edited a message they had sent you. Like {Revoke}, a control message: it
|
|
7
|
+
# names the message being replaced and carries the replacement content.
|
|
8
|
+
#
|
|
9
|
+
# Also arrives through {Whatsapp::Webhook::SmbMessageEchoes} when the business
|
|
10
|
+
# itself edits a message from the WhatsApp Business app.
|
|
11
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/reference/messages/edit
|
|
12
|
+
class Edit < Base
|
|
13
|
+
# @!attribute [rw] original_message_id
|
|
14
|
+
# @return [String, nil] The wamid of the message being replaced.
|
|
15
|
+
attr_accessor :original_message_id
|
|
16
|
+
|
|
17
|
+
# @!attribute [rw] message
|
|
18
|
+
# @return [Base, nil] The replacement content, dispatched by its own `type`
|
|
19
|
+
# through {Message.deserialize} like any other message.
|
|
20
|
+
#
|
|
21
|
+
# It carries *content only*. Meta's example gives the nested payload a `type`,
|
|
22
|
+
# its type body, and a `context`, but no `from`/`id`/`timestamp` of its own —
|
|
23
|
+
# those belong to the outer envelope, so the nested message's equivalents read
|
|
24
|
+
# `nil`. Reach for `edit.id` and `edit.from`, not `edit.message.id`.
|
|
25
|
+
attr_accessor :message
|
|
26
|
+
|
|
27
|
+
def initialize(original_message_id: nil, message: nil, **base_attributes)
|
|
28
|
+
super(**base_attributes)
|
|
29
|
+
|
|
30
|
+
@original_message_id = original_message_id
|
|
31
|
+
@message = message
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
# @param data [Hash] The raw message hash.
|
|
36
|
+
# @return [Edit]
|
|
37
|
+
def deserialize(data)
|
|
38
|
+
edit = data["edit"] || {}
|
|
39
|
+
|
|
40
|
+
new(
|
|
41
|
+
original_message_id: edit["original_message_id"],
|
|
42
|
+
message: edit["message"] ? Message.deserialize(edit["message"]) : nil,
|
|
43
|
+
**common_attributes(data)
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Whatsapp
|
|
4
|
+
module Webhook
|
|
5
|
+
class Message
|
|
6
|
+
# A user deleted a message they had sent you. A control message rather than a
|
|
7
|
+
# content one: it names a message you already received instead of carrying a body.
|
|
8
|
+
#
|
|
9
|
+
# Also arrives through {Whatsapp::Webhook::SmbMessageEchoes} when the business
|
|
10
|
+
# itself revokes a message from the WhatsApp Business app.
|
|
11
|
+
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/reference/messages/revoke
|
|
12
|
+
class Revoke < Base
|
|
13
|
+
# @!attribute [rw] original_message_id
|
|
14
|
+
# @return [String, nil] The wamid of the message that was deleted.
|
|
15
|
+
attr_accessor :original_message_id
|
|
16
|
+
|
|
17
|
+
def initialize(original_message_id: nil, **base_attributes)
|
|
18
|
+
super(**base_attributes)
|
|
19
|
+
|
|
20
|
+
@original_message_id = original_message_id
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
# @param data [Hash] The raw message hash.
|
|
25
|
+
# @return [Revoke]
|
|
26
|
+
def deserialize(data)
|
|
27
|
+
new(
|
|
28
|
+
original_message_id: data.dig("revoke", "original_message_id"),
|
|
29
|
+
**common_attributes(data)
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -17,16 +17,31 @@ module Whatsapp
|
|
|
17
17
|
# @return [String, nil] The customer's (possibly new) WhatsApp ID.
|
|
18
18
|
attr_accessor :wa_id
|
|
19
19
|
|
|
20
|
+
# @!attribute [rw] user_id
|
|
21
|
+
# @return [String, nil] The customer's new business-scoped user ID (BSUID),
|
|
22
|
+
# set when the change was a `user_changed_user_id` event.
|
|
23
|
+
attr_accessor :user_id
|
|
24
|
+
|
|
25
|
+
# @!attribute [rw] parent_user_id
|
|
26
|
+
# @return [String, nil] The customer's new parent BSUID, present only when enrolled.
|
|
27
|
+
attr_accessor :parent_user_id
|
|
28
|
+
|
|
20
29
|
# @!attribute [rw] change_type
|
|
21
|
-
# @return [String, nil] e.g. `"customer_changed_number"`, `"
|
|
30
|
+
# @return [String, nil] e.g. `"customer_changed_number"`, `"user_changed_user_id"`.
|
|
22
31
|
attr_accessor :change_type
|
|
23
32
|
|
|
24
|
-
def initialize(
|
|
33
|
+
def initialize(
|
|
34
|
+
change_type:,
|
|
35
|
+
body: nil, identity: nil, wa_id: nil, user_id: nil, parent_user_id: nil,
|
|
36
|
+
**base_attributes
|
|
37
|
+
)
|
|
25
38
|
super(**base_attributes)
|
|
26
39
|
|
|
27
40
|
@body = body
|
|
28
41
|
@identity = identity
|
|
29
42
|
@wa_id = wa_id
|
|
43
|
+
@user_id = user_id
|
|
44
|
+
@parent_user_id = parent_user_id
|
|
30
45
|
@change_type = change_type
|
|
31
46
|
end
|
|
32
47
|
|
|
@@ -38,6 +53,8 @@ module Whatsapp
|
|
|
38
53
|
body: data.dig("system", "body"),
|
|
39
54
|
identity: data.dig("system", "identity"),
|
|
40
55
|
wa_id: data.dig("system", "wa_id"),
|
|
56
|
+
user_id: data.dig("system", "user_id"),
|
|
57
|
+
parent_user_id: data.dig("system", "parent_user_id"),
|
|
41
58
|
change_type: data.dig("system", "type"),
|
|
42
59
|
**common_attributes(data)
|
|
43
60
|
)
|
|
@@ -7,9 +7,9 @@ module Whatsapp
|
|
|
7
7
|
# type Meta added after this gem was released, or a message the Cloud API
|
|
8
8
|
# itself couldn't process (carries an `errors[]` array in that case).
|
|
9
9
|
class Unknown < Base
|
|
10
|
-
# @!attribute [
|
|
10
|
+
# @!attribute [r] raw
|
|
11
11
|
# @return [Hash] The full, untyped raw message hash.
|
|
12
|
-
|
|
12
|
+
attr_reader :raw
|
|
13
13
|
|
|
14
14
|
# @!attribute [rw] errors
|
|
15
15
|
# @return [Array<Error>]
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module Whatsapp
|
|
4
4
|
module Webhook
|
|
5
|
-
# The `messages` webhook field's `value` payload —
|
|
6
|
-
# confirmed, JSON-example-backed schema in Meta's docs
|
|
7
|
-
# messages and/or delivery status
|
|
5
|
+
# The `messages` webhook field's `value` payload — one of five fields with a
|
|
6
|
+
# confirmed, JSON-example-backed schema in Meta's docs, alongside {UserIdUpdate}
|
|
7
|
+
# and {BusinessUsernameUpdates}. Carries inbound messages and/or delivery status
|
|
8
|
+
# updates for one of your business phone numbers.
|
|
8
9
|
# Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/overview
|
|
9
10
|
class Messages
|
|
10
11
|
# @!attribute [rw] messaging_product
|