waha-ruby 0.1.1 → 0.2.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 +36 -1
- data/README.md +15 -0
- data/lib/waha/client.rb +3 -0
- data/lib/waha/configuration.rb +33 -0
- data/lib/waha/error.rb +10 -0
- data/lib/waha/rails/session_state.rb +24 -0
- data/lib/waha/rails.rb +5 -0
- data/lib/waha/resources/chats.rb +11 -0
- data/lib/waha/resources/groups.rb +28 -0
- data/lib/waha/resources/messages.rb +36 -15
- data/lib/waha/resources/sessions.rb +33 -9
- data/lib/waha/transport/faraday.rb +8 -1
- data/lib/waha/version.rb +1 -1
- data/lib/waha.rb +48 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 36cfca6cd9f43620fcc84fe54c821262683471a4a56c74f8f757e07044e3cc46
|
|
4
|
+
data.tar.gz: f393af7a3285bfc18fa6684c943d6aae31046c283447f960ba37e571c99dce6f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 621b3f6e2a63cf06ff933cad60c07e09dafd9b6d3eea473d718604a6ff8f7f580bea029e21e3abc074996b9804141b9c4f16d2afdef2869a38badd12d6985050
|
|
7
|
+
data.tar.gz: 0b3f6c7db0e5c4ec5222021d445334d45bf53af6fbb2b766555633ccdfd560cd15ca167a87287b8ba3e2dae3ddb405afa37181f4848c80c8817fcd2a13eb5900
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,39 @@ and this project adheres to
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.2.0] - 2026-09-16
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `Waha.configure`, `Waha.configuration`, `Waha.configured?`, and
|
|
16
|
+
`Waha.client(session:, **overrides)` for opt-in process-wide defaults.
|
|
17
|
+
Every call builds a fresh client; instances are never cached.
|
|
18
|
+
- `Waha.valid_chat_id?` for `@c.us`, `@g.us`, and `@lid` ids, plus
|
|
19
|
+
`Waha.group_chat_id` to append the `@g.us` suffix when absent.
|
|
20
|
+
- `Waha::Resources::Chats#list` for `GET /api/{session}/chats`.
|
|
21
|
+
- `Waha::Resources::Groups` (`client.groups`) with `list` and `get`.
|
|
22
|
+
- `Waha::Resources::Sessions#update` (`PUT /api/sessions/{name}`).
|
|
23
|
+
- `Waha::Resources::Sessions#qr_data_url`, returning the QR PNG as a
|
|
24
|
+
browser-ready `data:image/png;base64,` URL.
|
|
25
|
+
- `Waha::Resources::Sessions#ready?`, a WORKING-status predicate.
|
|
26
|
+
- `Waha::ServerError` (>= 500) and `Waha::RateLimitError` (429), plus
|
|
27
|
+
`Waha::Error#retryable?` covering transport failures, 408, and 429.
|
|
28
|
+
- Opt-in Rails adapter `Waha::Rails::SessionState` (aliased as
|
|
29
|
+
`Waha::SessionState`), which memoizes session readiness in `Rails.cache`.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- `Waha::Resources::Messages#send_image`, `#send_file`, and `#send_voice`
|
|
34
|
+
now accept `mimetype: nil`: the MIME type is read from the data URL prefix
|
|
35
|
+
when omitted, and left out of the payload for remote URLs so WAHA can
|
|
36
|
+
detect it.
|
|
37
|
+
- Text, media, seen, and typing requests accept both `200` and `201`
|
|
38
|
+
responses across WAHA engines.
|
|
39
|
+
- `Waha::Resources::Sessions#qr` treats `format: "image"` as binary, not JSON.
|
|
40
|
+
- `Waha::Client` exposes `session`.
|
|
41
|
+
|
|
42
|
+
## [0.1.1] - 2026-09-09
|
|
43
|
+
|
|
11
44
|
### Fixed
|
|
12
45
|
|
|
13
46
|
- `Waha::Webhook.verify!` fails closed when the secret is nil, empty, or
|
|
@@ -44,5 +77,7 @@ and this project adheres to
|
|
|
44
77
|
install generator for the `waha.rb` initializer.
|
|
45
78
|
- Typed `Waha::Error` (operation, status, bounded redacted details) with
|
|
46
79
|
provider payload redaction.
|
|
47
|
-
[Unreleased]: https://github.com/develoz-com/waha-ruby/compare/v0.
|
|
80
|
+
[Unreleased]: https://github.com/develoz-com/waha-ruby/compare/v0.2.0...HEAD
|
|
81
|
+
[0.2.0]: https://github.com/develoz-com/waha-ruby/compare/v0.1.1...v0.2.0
|
|
82
|
+
[0.1.1]: https://github.com/develoz-com/waha-ruby/compare/v0.1.0...v0.1.1
|
|
48
83
|
[0.1.0]: https://github.com/develoz-com/waha-ruby/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -39,6 +39,21 @@ client.messages.send_text(chat_id: "5511999999999@c.us", text: "Olá")
|
|
|
39
39
|
|
|
40
40
|
Every request uses the client's default session unless a resource method accepts an explicit `session:` override. Keep credentials in environment variables or a secret manager; do not commit them.
|
|
41
41
|
|
|
42
|
+
Applications that prefer process-wide defaults can configure the module once and build a fresh client per call:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
Waha.configure do |config|
|
|
46
|
+
config.base_url = ENV.fetch("WAHA_BASE_URL")
|
|
47
|
+
config.api_key = ENV["WAHA_API_KEY"]
|
|
48
|
+
config.session = ENV.fetch("WAHA_SESSION", "default")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
Waha.configured? # => true
|
|
52
|
+
Waha.client(session: "default") # a new Waha::Client, never cached
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`Waha.client` never caches instances, so per-call session overrides cannot leak across callers.
|
|
56
|
+
|
|
42
57
|
## API guide
|
|
43
58
|
|
|
44
59
|
The complete v0.1 resource map, argument conventions, raw return shapes, file payloads, errors, GOWS helpers, webhook verification, and Rails integration are in [docs/api.md](docs/api.md). For setup and secret handling, see [docs/installation.md](docs/installation.md).
|
data/lib/waha/client.rb
CHANGED
|
@@ -6,6 +6,7 @@ module Waha
|
|
|
6
6
|
sessions: Resources::Sessions,
|
|
7
7
|
messages: Resources::Messages,
|
|
8
8
|
chats: Resources::Chats,
|
|
9
|
+
groups: Resources::Groups,
|
|
9
10
|
contacts: Resources::Contacts,
|
|
10
11
|
presence: Resources::Presence,
|
|
11
12
|
webhooks: Resources::Webhooks,
|
|
@@ -19,6 +20,8 @@ module Waha
|
|
|
19
20
|
@resources = {}
|
|
20
21
|
end
|
|
21
22
|
|
|
23
|
+
attr_reader :session
|
|
24
|
+
|
|
22
25
|
RESOURCES.each do |name, resource_class|
|
|
23
26
|
define_method(name) do
|
|
24
27
|
@resources[name] ||= build_resource(name, resource_class)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Waha
|
|
4
|
+
DEFAULT_TIMEOUT = 30
|
|
5
|
+
DEFAULT_SESSION = "default"
|
|
6
|
+
|
|
7
|
+
# Process-wide defaults for Waha.client. Rails applications can set these in
|
|
8
|
+
# an initializer; framework-neutral callers can pass keywords to Waha.client.
|
|
9
|
+
# No client instances are cached here.
|
|
10
|
+
Configuration = Struct.new(:base_url, :api_key, :session, :timeout) do
|
|
11
|
+
def initialize(base_url: nil, api_key: nil, session: nil, timeout: nil)
|
|
12
|
+
super(
|
|
13
|
+
base_url || default_base_url,
|
|
14
|
+
api_key || ENV.fetch("WAHA_API_KEY", nil),
|
|
15
|
+
session || default_session,
|
|
16
|
+
timeout || DEFAULT_TIMEOUT
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
alias_method :session_name, :session
|
|
21
|
+
alias_method :session_name=, :session=
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def default_base_url
|
|
26
|
+
ENV.fetch("WAHA_BASE_URL", nil) || ENV.fetch("WAHA_API_URL", nil)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def default_session
|
|
30
|
+
ENV.fetch("WAHA_SESSION", nil) || ENV.fetch("WAHA_SESSION_NAME", nil) || DEFAULT_SESSION
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/waha/error.rb
CHANGED
|
@@ -5,6 +5,8 @@ module Waha
|
|
|
5
5
|
MESSAGE_LIMIT = 600
|
|
6
6
|
DETAILS_LIMIT = 450
|
|
7
7
|
|
|
8
|
+
RETRYABLE_STATUSES = [408, 429].freeze
|
|
9
|
+
|
|
8
10
|
attr_reader :operation, :status, :details
|
|
9
11
|
|
|
10
12
|
def initialize(operation:, status: nil, details: nil)
|
|
@@ -14,6 +16,12 @@ module Waha
|
|
|
14
16
|
super(build_message.slice(0, MESSAGE_LIMIT))
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
# Retryable failures: transport problems, WAHA server errors, rate limits, timeouts.
|
|
20
|
+
def retryable?
|
|
21
|
+
is_a?(ServerError) || is_a?(RateLimitError) || is_a?(TransportError) ||
|
|
22
|
+
RETRYABLE_STATUSES.include?(status)
|
|
23
|
+
end
|
|
24
|
+
|
|
17
25
|
private
|
|
18
26
|
|
|
19
27
|
def sanitize(details)
|
|
@@ -30,6 +38,8 @@ module Waha
|
|
|
30
38
|
end
|
|
31
39
|
|
|
32
40
|
class ApiError < Error; end
|
|
41
|
+
class ServerError < ApiError; end
|
|
42
|
+
class RateLimitError < ApiError; end
|
|
33
43
|
class TransportError < Error; end
|
|
34
44
|
class ValidationError < Error; end
|
|
35
45
|
class VerificationError < Error; end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Waha
|
|
4
|
+
module Rails
|
|
5
|
+
module SessionState
|
|
6
|
+
CACHE_KEY_PREFIX = "waha:session_active"
|
|
7
|
+
CACHE_TTL = 30
|
|
8
|
+
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def active?(session_name: Waha.configuration.session_name)
|
|
12
|
+
return false unless Waha.configured?
|
|
13
|
+
|
|
14
|
+
::Rails.cache.fetch(cache_key(session_name), expires_in: CACHE_TTL) do
|
|
15
|
+
Waha.client(session: session_name).sessions.ready?
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def cache_key(session_name)
|
|
20
|
+
"#{CACHE_KEY_PREFIX}:#{session_name}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/waha/rails.rb
CHANGED
|
@@ -4,5 +4,10 @@ require "waha"
|
|
|
4
4
|
require "active_support"
|
|
5
5
|
require "rails/railtie"
|
|
6
6
|
require_relative "rails/controller_concern"
|
|
7
|
+
require_relative "rails/session_state"
|
|
7
8
|
require_relative "rails/railtie"
|
|
8
9
|
require_relative "../generators/waha/install/install_generator"
|
|
10
|
+
|
|
11
|
+
module Waha
|
|
12
|
+
SessionState = Waha::Rails::SessionState
|
|
13
|
+
end
|
data/lib/waha/resources/chats.rb
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
module Waha
|
|
4
4
|
module Resources
|
|
5
5
|
class Chats < Resource
|
|
6
|
+
def list(session: nil, **query)
|
|
7
|
+
name = session_name(session, "list_chats")
|
|
8
|
+
transport.request(
|
|
9
|
+
method: :get,
|
|
10
|
+
path: "/api/#{segment(name)}/chats",
|
|
11
|
+
operation: "list_chats",
|
|
12
|
+
expected_status: 200,
|
|
13
|
+
query: Support.query_hash(Support.compact_hash(query))
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
6
17
|
def overview(ids: nil, limit: nil, offset: nil, session: nil, **query)
|
|
7
18
|
name = session_name(session, "chats_overview")
|
|
8
19
|
transport.request(
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Waha
|
|
4
|
+
module Resources
|
|
5
|
+
class Groups < Resource
|
|
6
|
+
def list(session: nil, **query)
|
|
7
|
+
name = session_name(session, "list_groups")
|
|
8
|
+
transport.request(
|
|
9
|
+
method: :get,
|
|
10
|
+
path: "/api/#{segment(name)}/groups",
|
|
11
|
+
operation: "list_groups",
|
|
12
|
+
expected_status: 200,
|
|
13
|
+
query: Support.query_hash(Support.compact_hash(query))
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def get(group_id:, session: nil)
|
|
18
|
+
name = session_name(session, "get_group")
|
|
19
|
+
transport.request(
|
|
20
|
+
method: :get,
|
|
21
|
+
path: "/api/#{segment(name)}/groups/#{segment(group_id)}",
|
|
22
|
+
operation: "get_group",
|
|
23
|
+
expected_status: 200
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -7,6 +7,8 @@ module Waha
|
|
|
7
7
|
class Messages < Resource
|
|
8
8
|
DATA_MEDIA_CHARS = "a-zA-Z0-9!/:[:space:]\u0023$&'()*+,;=?~_-"
|
|
9
9
|
DATA_URL_PATTERN = Regexp.new("\\Adata:[#{DATA_MEDIA_CHARS}]*;base64,", Regexp::IGNORECASE)
|
|
10
|
+
DATA_URL_MIMETYPE_PATTERN = Regexp.new("\\Adata:([^;,]+);base64,", Regexp::IGNORECASE)
|
|
11
|
+
SUCCESS_STATUSES = [200, 201].freeze
|
|
10
12
|
|
|
11
13
|
def send_text(chat_id:, text:, id: nil, reply_to: nil, session: nil)
|
|
12
14
|
body = { session: session_name(session, "send_text"), chatId: chat_id, text: }
|
|
@@ -16,7 +18,7 @@ module Waha
|
|
|
16
18
|
method: :post,
|
|
17
19
|
path: "/api/sendText",
|
|
18
20
|
operation: "send_text",
|
|
19
|
-
expected_status:
|
|
21
|
+
expected_status: SUCCESS_STATUSES,
|
|
20
22
|
body:
|
|
21
23
|
)
|
|
22
24
|
end
|
|
@@ -62,7 +64,7 @@ module Waha
|
|
|
62
64
|
method: :post,
|
|
63
65
|
path: "/api/sendSeen",
|
|
64
66
|
operation: "send_seen",
|
|
65
|
-
expected_status:
|
|
67
|
+
expected_status: SUCCESS_STATUSES,
|
|
66
68
|
body:
|
|
67
69
|
)
|
|
68
70
|
end
|
|
@@ -85,36 +87,36 @@ module Waha
|
|
|
85
87
|
)
|
|
86
88
|
end
|
|
87
89
|
|
|
88
|
-
def send_image(chat_id:, file:, mimetype
|
|
90
|
+
def send_image(chat_id:, file:, mimetype: nil, filename: nil, caption: nil, reply_to: nil, session: nil)
|
|
89
91
|
body = file_body("send_image", { chat_id:, file:, mimetype:, filename:, caption:, reply_to:, session: })
|
|
90
92
|
transport.request(
|
|
91
93
|
method: :post,
|
|
92
94
|
path: "/api/sendImage",
|
|
93
95
|
operation: "send_image",
|
|
94
|
-
expected_status:
|
|
96
|
+
expected_status: SUCCESS_STATUSES,
|
|
95
97
|
body:
|
|
96
98
|
)
|
|
97
99
|
end
|
|
98
100
|
|
|
99
|
-
def send_file(chat_id:, file:, mimetype
|
|
101
|
+
def send_file(chat_id:, file:, mimetype: nil, filename: nil, caption: nil, reply_to: nil, session: nil)
|
|
100
102
|
body = file_body("send_file", { chat_id:, file:, mimetype:, filename:, caption:, reply_to:, session: })
|
|
101
103
|
transport.request(
|
|
102
104
|
method: :post,
|
|
103
105
|
path: "/api/sendFile",
|
|
104
106
|
operation: "send_file",
|
|
105
|
-
expected_status:
|
|
107
|
+
expected_status: SUCCESS_STATUSES,
|
|
106
108
|
body:
|
|
107
109
|
)
|
|
108
110
|
end
|
|
109
111
|
|
|
110
|
-
def send_voice(chat_id:, file:, mimetype
|
|
112
|
+
def send_voice(chat_id:, file:, mimetype: nil, filename: nil, reply_to: nil, convert: nil, session: nil)
|
|
111
113
|
body = file_body("send_voice", { chat_id:, file:, mimetype:, filename:, reply_to:, session: })
|
|
112
114
|
body[:convert] = convert unless convert.nil?
|
|
113
115
|
transport.request(
|
|
114
116
|
method: :post,
|
|
115
117
|
path: "/api/sendVoice",
|
|
116
118
|
operation: "send_voice",
|
|
117
|
-
expected_status:
|
|
119
|
+
expected_status: SUCCESS_STATUSES,
|
|
118
120
|
body:
|
|
119
121
|
)
|
|
120
122
|
end
|
|
@@ -139,20 +141,39 @@ module Waha
|
|
|
139
141
|
|
|
140
142
|
def file_payload(operation, file:, mimetype:, filename:)
|
|
141
143
|
raise ValidationError.new(operation:, details: "file must be a non-empty string") if file.to_s.empty?
|
|
142
|
-
raise ValidationError.new(operation:, details: "mimetype is required") if mimetype.to_s.empty?
|
|
143
|
-
|
|
144
|
-
payload = { mimetype: }
|
|
145
|
-
payload[:filename] = filename unless filename.nil?
|
|
146
144
|
|
|
147
145
|
if http_url?(file)
|
|
148
|
-
|
|
146
|
+
remote_file_payload(file, mimetype, filename)
|
|
149
147
|
elsif data_url?(file)
|
|
150
|
-
|
|
148
|
+
inline_file_payload(operation, file, mimetype, filename)
|
|
151
149
|
else
|
|
152
150
|
raise ValidationError.new(operation:, details: "file must be an http(s) URL or data URL")
|
|
153
151
|
end
|
|
154
152
|
end
|
|
155
153
|
|
|
154
|
+
# WAHA detects the MIME type of remote URLs; an explicit mimetype wins.
|
|
155
|
+
def remote_file_payload(file, mimetype, filename)
|
|
156
|
+
payload = {}
|
|
157
|
+
payload[:mimetype] = mimetype unless mimetype.to_s.empty?
|
|
158
|
+
payload[:filename] = filename unless filename.nil?
|
|
159
|
+
payload.merge(url: file)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Data URLs carry their MIME type in the prefix when none is supplied.
|
|
163
|
+
def inline_file_payload(operation, file, mimetype, filename)
|
|
164
|
+
resolved_mimetype = mimetype.to_s.empty? ? data_url_mimetype(file) : mimetype
|
|
165
|
+
raise ValidationError.new(operation:, details: "mimetype is required") if resolved_mimetype.to_s.empty?
|
|
166
|
+
|
|
167
|
+
payload = { mimetype: resolved_mimetype }
|
|
168
|
+
payload[:filename] = filename unless filename.nil?
|
|
169
|
+
payload.merge(data: file.split(",", 2).last)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def data_url_mimetype(value)
|
|
173
|
+
match = DATA_URL_MIMETYPE_PATTERN.match(value.to_s)
|
|
174
|
+
match && match[1]
|
|
175
|
+
end
|
|
176
|
+
|
|
156
177
|
def http_url?(value)
|
|
157
178
|
uri = URI.parse(value.to_s)
|
|
158
179
|
uri.is_a?(URI::HTTP) && !uri.host.to_s.empty?
|
|
@@ -169,7 +190,7 @@ module Waha
|
|
|
169
190
|
method: :post,
|
|
170
191
|
path: "/api/#{endpoint}",
|
|
171
192
|
operation:,
|
|
172
|
-
expected_status:
|
|
193
|
+
expected_status: SUCCESS_STATUSES,
|
|
173
194
|
body: { session: session_name(session, operation), chatId: chat_id }
|
|
174
195
|
)
|
|
175
196
|
end
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "base64"
|
|
4
|
+
|
|
3
5
|
module Waha
|
|
4
6
|
module Resources
|
|
5
7
|
class Sessions < Resource
|
|
8
|
+
WORKING_STATUS = "WORKING"
|
|
9
|
+
|
|
6
10
|
def list(all: nil)
|
|
7
11
|
transport.request(
|
|
8
12
|
method: :get,
|
|
@@ -24,13 +28,13 @@ module Waha
|
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
def create(name:, start: nil, config: nil)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
write_session("create_session", :post, "/api/sessions", { name:, start:, config: })
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def update(session: nil, name: nil, config: nil)
|
|
35
|
+
session_key = session_name(session, "update_session")
|
|
36
|
+
write_session("update_session", :put, "/api/sessions/#{segment(session_key)}",
|
|
37
|
+
{ name: name || session_key, config: })
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def start(session: nil)
|
|
@@ -61,17 +65,29 @@ module Waha
|
|
|
61
65
|
|
|
62
66
|
def qr(format: nil, session: nil)
|
|
63
67
|
name = session_name(session, "session_qr")
|
|
64
|
-
response_format = format.nil? ? :binary : :json
|
|
65
68
|
transport.request(
|
|
66
69
|
method: :get,
|
|
67
70
|
path: "/api/#{segment(name)}/auth/qr",
|
|
68
71
|
operation: "session_qr",
|
|
69
72
|
expected_status: 200,
|
|
70
73
|
query: Support.compact_hash(format: format),
|
|
71
|
-
response:
|
|
74
|
+
response: qr_response_format(format)
|
|
72
75
|
)
|
|
73
76
|
end
|
|
74
77
|
|
|
78
|
+
# PNG bytes rendered as a browser-ready data URL.
|
|
79
|
+
def qr_data_url(session: nil)
|
|
80
|
+
png = qr(format: "image", session:)
|
|
81
|
+
"data:image/png;base64,#{Base64.strict_encode64(png.to_s)}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# WAHA reports WORKING for an authenticated, usable session.
|
|
85
|
+
def ready?(session: nil)
|
|
86
|
+
Support.value(get(session:), "status") == WORKING_STATUS
|
|
87
|
+
rescue Waha::Error
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
|
|
75
91
|
def request_code(phone_number:, method: nil, session: nil)
|
|
76
92
|
name = session_name(session, "request_code")
|
|
77
93
|
transport.request(
|
|
@@ -85,6 +101,14 @@ module Waha
|
|
|
85
101
|
|
|
86
102
|
private
|
|
87
103
|
|
|
104
|
+
def write_session(operation, method, path, body)
|
|
105
|
+
transport.request(method:, path:, operation:, expected_status: [200, 201], body: Support.compact_hash(body))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def qr_response_format(format)
|
|
109
|
+
format.to_s == "image" || format.nil? ? :binary : :json
|
|
110
|
+
end
|
|
111
|
+
|
|
88
112
|
def action(operation, action, session:, body: nil)
|
|
89
113
|
name = session_name(session, operation)
|
|
90
114
|
transport.request(
|
|
@@ -101,7 +101,14 @@ module Waha
|
|
|
101
101
|
return if Array(expected_status).include?(response.status)
|
|
102
102
|
|
|
103
103
|
details = response.body.to_s.empty? ? "empty provider error response" : "[REDACTED]"
|
|
104
|
-
raise
|
|
104
|
+
raise error_for(response.status).new(operation:, status: response.status, details:)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def error_for(status)
|
|
108
|
+
return RateLimitError if status == 429
|
|
109
|
+
return ServerError if status.to_i >= 500
|
|
110
|
+
|
|
111
|
+
ApiError
|
|
105
112
|
end
|
|
106
113
|
|
|
107
114
|
def parse_response(response, format, operation)
|
data/lib/waha/version.rb
CHANGED
data/lib/waha.rb
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
require "waha/version"
|
|
4
4
|
require "waha/error"
|
|
5
5
|
require "waha/support"
|
|
6
|
+
require "waha/configuration"
|
|
6
7
|
require "waha/transport/faraday"
|
|
7
8
|
require "waha/resource"
|
|
8
9
|
require "waha/resources/sessions"
|
|
9
10
|
require "waha/resources/messages"
|
|
10
11
|
require "waha/resources/chats"
|
|
12
|
+
require "waha/resources/groups"
|
|
11
13
|
require "waha/resources/contacts"
|
|
12
14
|
require "waha/resources/presence"
|
|
13
15
|
require "waha/resources/webhooks"
|
|
@@ -15,3 +17,49 @@ require "waha/resources/media"
|
|
|
15
17
|
require "waha/gows"
|
|
16
18
|
require "waha/webhook"
|
|
17
19
|
require "waha/client"
|
|
20
|
+
|
|
21
|
+
module Waha
|
|
22
|
+
CHAT_ID_PATTERN = /\A[\d-]+@(?:[cg]\.us|lid)\z/
|
|
23
|
+
GROUP_CHAT_ID_SUFFIX = "@g.us"
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
def configuration
|
|
27
|
+
@configuration ||= Configuration.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def configure
|
|
31
|
+
yield(configuration)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Resets configuration; primarily useful for tests and forked workers.
|
|
35
|
+
def reset_configuration!
|
|
36
|
+
@configuration = Configuration.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def configured?
|
|
40
|
+
!configuration.base_url.to_s.strip.empty?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Builds a fresh client per call. Client instances are never cached so
|
|
44
|
+
# per-request session overrides can never leak across callers.
|
|
45
|
+
def client(session: nil, **overrides)
|
|
46
|
+
Waha::Client.new(
|
|
47
|
+
base_url: overrides.fetch(:base_url, configuration.base_url),
|
|
48
|
+
api_key: overrides.fetch(:api_key, configuration.api_key),
|
|
49
|
+
session: session || overrides.fetch(:session, configuration.session),
|
|
50
|
+
timeout: overrides.fetch(:timeout, configuration.timeout)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# True for contact (@c.us), group (@g.us), and lid (@lid) chat IDs.
|
|
55
|
+
def valid_chat_id?(chat_id)
|
|
56
|
+
!chat_id.to_s.strip.empty? && CHAT_ID_PATTERN.match?(chat_id.to_s)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Appends the @g.us suffix when absent so bare numeric group IDs are usable.
|
|
60
|
+
def group_chat_id(id)
|
|
61
|
+
text = id.to_s
|
|
62
|
+
text.end_with?(GROUP_CHAT_ID_SUFFIX) ? text : "#{text}#{GROUP_CHAT_ID_SUFFIX}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: waha-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mauricio Zaffari
|
|
@@ -229,14 +229,17 @@ files:
|
|
|
229
229
|
- lib/generators/waha/install/templates/waha.rb
|
|
230
230
|
- lib/waha.rb
|
|
231
231
|
- lib/waha/client.rb
|
|
232
|
+
- lib/waha/configuration.rb
|
|
232
233
|
- lib/waha/error.rb
|
|
233
234
|
- lib/waha/gows.rb
|
|
234
235
|
- lib/waha/rails.rb
|
|
235
236
|
- lib/waha/rails/controller_concern.rb
|
|
236
237
|
- lib/waha/rails/railtie.rb
|
|
238
|
+
- lib/waha/rails/session_state.rb
|
|
237
239
|
- lib/waha/resource.rb
|
|
238
240
|
- lib/waha/resources/chats.rb
|
|
239
241
|
- lib/waha/resources/contacts.rb
|
|
242
|
+
- lib/waha/resources/groups.rb
|
|
240
243
|
- lib/waha/resources/media.rb
|
|
241
244
|
- lib/waha/resources/messages.rb
|
|
242
245
|
- lib/waha/resources/presence.rb
|
|
@@ -251,7 +254,7 @@ licenses:
|
|
|
251
254
|
- MIT
|
|
252
255
|
metadata:
|
|
253
256
|
homepage_uri: https://github.com/develoz-com/waha-ruby
|
|
254
|
-
source_code_uri: https://github.com/develoz-com/waha-ruby/tree/v0.
|
|
257
|
+
source_code_uri: https://github.com/develoz-com/waha-ruby/tree/v0.2.0
|
|
255
258
|
changelog_uri: https://github.com/develoz-com/waha-ruby/blob/main/CHANGELOG.md
|
|
256
259
|
rubygems_mfa_required: 'true'
|
|
257
260
|
rdoc_options: []
|