chat_sdk-whatsapp 1.0.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37aa0b2742ef4e9ba92c68df7ad0650b53e427cbe41b9f4c0b387fd479f8bef1
4
- data.tar.gz: 3bda0cd18ee75406ad07085e28d7ee61e92af98061a2761c0e8c8847c87a1fa3
3
+ metadata.gz: 56d2740433f6135cc084664554536e8aada5dadc9784f495e63b744d572bb1b1
4
+ data.tar.gz: 97cae7132efd2af77e10be202134f1430b0142477912745158294c7d297afc22
5
5
  SHA512:
6
- metadata.gz: ddc966c4e7597d80cac325cadd8f860fc886f9e47ac8c2647ea395f665505648a2f9633bf8362580507c7e99b969c7bf8cc7736836a3ab10d56cab96cb07c767
7
- data.tar.gz: d5977f541e427eb4a68939489004f35eecb407201d4a057f6bc00afb4cbf0b1a5114c313ca21611da4eafd35550aad3ca6f1249b2bf994380887d2d793c6a997
6
+ metadata.gz: 58f760deb91563a1cd2cec6a32725fbdecf1249ad6116509ac6646321b36d116b0630220553c2b00c6dde71f8eb9583570cc6a02ffe6d9c386c47827091120a2
7
+ data.tar.gz: 4475cb8cec45232adfe4a1090b08960071a8f041753b2e75d15f977791f35894285e2bd728be042aca550913e89fda9a87d9abadd0af0bc8fe1b700b9797173b
@@ -6,7 +6,7 @@ module ChatSDK
6
6
  include ChatSDK::Adapter::MetaVerification
7
7
  include ChatSDK::Adapter::MediaTypes
8
8
 
9
- capabilities :direct_messages, :file_uploads, :reactions
9
+ capabilities :direct_messages, :file_uploads, :reactions, :replies, :read_receipts
10
10
 
11
11
  attr_reader :client
12
12
 
@@ -92,10 +92,26 @@ module ChatSDK
92
92
  @client.send_template(to: channel_id, template_name: template_name, language_code: language_code, components: components)
93
93
  end
94
94
 
95
- def mark_as_read(message_id:)
95
+ def mark_as_read(message_id:, channel_id: nil, thread_id: nil, message: nil) # rubocop:disable Lint/UnusedMethodArgument
96
96
  @client.mark_as_read(message_id: message_id)
97
97
  end
98
98
 
99
+ def reply_message(channel_id:, message_id:, message:, thread_id: nil) # rubocop:disable Lint/UnusedMethodArgument
100
+ payload = prepare_message_payload(message)
101
+ payload[:context] = {message_id: message_id}
102
+ result = @client.send_message(to: channel_id, **payload)
103
+ parse_whatsapp_message(result, channel_id)
104
+ end
105
+
106
+ def download_media(media_id:)
107
+ meta = @client.get_media_url(media_id: media_id)
108
+ url = meta["url"]
109
+ return nil unless url
110
+
111
+ response = @client.download_media(url: url)
112
+ response.body
113
+ end
114
+
99
115
  def upload_file(channel_id:, io:, filename:, thread_id: nil, comment: nil) # rubocop:disable Lint/UnusedMethodArgument
100
116
  content_type = detect_content_type(filename)
101
117
  media_type = media_type_for(content_type)
@@ -1,9 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "uri"
4
+
3
5
  module ChatSDK
4
6
  module WhatsApp
5
7
  class ApiClient < ChatSDK::ApiClient::Base
6
8
  BASE_URL = "https://graph.facebook.com/v25.0"
9
+ MEDIA_HOSTS = %w[fbcdn.net fbsbx.com].freeze
10
+ MEDIA_DOWNLOAD_LIMIT = 25 * 1024 * 1024
11
+ MEDIA_DOWNLOAD_TIMEOUT = 30
7
12
 
8
13
  def initialize(access_token, phone_number_id)
9
14
  @access_token = access_token
@@ -51,6 +56,19 @@ module ChatSDK
51
56
  })
52
57
  end
53
58
 
59
+ def get_media_url(media_id:)
60
+ request(:get, media_id.to_s)
61
+ end
62
+
63
+ def download_media(url:)
64
+ validate_media_url!(url)
65
+ Faraday.get(url) do |req|
66
+ req.headers["Authorization"] = "Bearer #{@access_token}"
67
+ req.options.timeout = MEDIA_DOWNLOAD_TIMEOUT
68
+ end
69
+ .tap { |response| validate_media_response!(response) }
70
+ end
71
+
54
72
  def upload_media(io:, filename:, content_type:)
55
73
  response = upload_connection.post("#{@phone_number_id}/media") do |req|
56
74
  req.body = {
@@ -77,6 +95,34 @@ module ChatSDK
77
95
  faraday.headers["Authorization"] = "Bearer #{@access_token}"
78
96
  end
79
97
 
98
+ def validate_media_url!(url)
99
+ uri = URI.parse(url)
100
+ host = uri.host&.downcase
101
+ graph_origin = URI.parse(BASE_URL)
102
+ trusted_host = host == graph_origin.host || MEDIA_HOSTS.any? { |allowed| host == allowed || host&.end_with?(".#{allowed}") }
103
+ trusted = uri.is_a?(URI::HTTPS) && uri.port == 443 && trusted_host
104
+ return if trusted
105
+
106
+ raise ChatSDK::PlatformError.new(
107
+ "Refusing to send the WhatsApp access token to an untrusted media URL",
108
+ adapter_name: :whatsapp
109
+ )
110
+ rescue URI::InvalidURIError
111
+ raise ChatSDK::PlatformError.new("Invalid WhatsApp media URL", adapter_name: :whatsapp)
112
+ end
113
+
114
+ def validate_media_response!(response)
115
+ declared_size = response.headers["content-length"]&.to_i
116
+ actual_size = response.body.respond_to?(:bytesize) ? response.body.bytesize : 0
117
+ return if [declared_size, actual_size].compact.max <= MEDIA_DOWNLOAD_LIMIT
118
+
119
+ raise ChatSDK::PlatformError.new(
120
+ "WhatsApp media exceeds the 25 MB download limit",
121
+ status: response.status,
122
+ adapter_name: :whatsapp
123
+ )
124
+ end
125
+
80
126
  def extract_error_message(response)
81
127
  body = response.body
82
128
  body.is_a?(Hash) ? body.dig("error", "message") : response.status.to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chat_sdk-whatsapp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Quentin Rousseau