whatsapp_notifier 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 +7 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +126 -0
- data/Rakefile +6 -0
- data/bin/whatsapp_notifier +53 -0
- data/docs/bulk_messaging_policy.md +30 -0
- data/docs/graphify.md +108 -0
- data/docs/rails_setup.md +57 -0
- data/examples/notification_example.rb +14 -0
- data/lib/generators/whatsapp_notifier/install_generator.rb +60 -0
- data/lib/generators/whatsapp_notifier/install_service_generator.rb +33 -0
- data/lib/generators/whatsapp_notifier/templates/whatsapp_notifier.rb +6 -0
- data/lib/whatsapp_notifier/bulk/dispatcher.rb +64 -0
- data/lib/whatsapp_notifier/bulk/rate_limiter.rb +17 -0
- data/lib/whatsapp_notifier/bulk/retry_policy.rb +32 -0
- data/lib/whatsapp_notifier/client.rb +43 -0
- data/lib/whatsapp_notifier/configuration.rb +41 -0
- data/lib/whatsapp_notifier/doctor.rb +103 -0
- data/lib/whatsapp_notifier/errors.rb +5 -0
- data/lib/whatsapp_notifier/jobs/send_message_job.rb +20 -0
- data/lib/whatsapp_notifier/notification.rb +93 -0
- data/lib/whatsapp_notifier/providers/base.rb +24 -0
- data/lib/whatsapp_notifier/providers/web_automation.rb +85 -0
- data/lib/whatsapp_notifier/railtie.rb +14 -0
- data/lib/whatsapp_notifier/result.rb +23 -0
- data/lib/whatsapp_notifier/services/web_automation/bun.lock +452 -0
- data/lib/whatsapp_notifier/services/web_automation/index.ts +285 -0
- data/lib/whatsapp_notifier/services/web_automation/package.json +14 -0
- data/lib/whatsapp_notifier/session/qr_service.rb +51 -0
- data/lib/whatsapp_notifier/session/store.rb +22 -0
- data/lib/whatsapp_notifier/version.rb +4 -0
- data/lib/whatsapp_notifier/web_adapter.rb +72 -0
- data/lib/whatsapp_notifier.rb +72 -0
- data/spec/bulk/dispatcher_spec.rb +73 -0
- data/spec/bulk/rate_limiter_spec.rb +27 -0
- data/spec/bulk/retry_policy_spec.rb +33 -0
- data/spec/client_spec.rb +52 -0
- data/spec/configuration_spec.rb +47 -0
- data/spec/doctor_spec.rb +46 -0
- data/spec/jobs/send_message_job_spec.rb +36 -0
- data/spec/notification_spec.rb +60 -0
- data/spec/providers/base_spec.rb +17 -0
- data/spec/providers/web_automation_spec.rb +109 -0
- data/spec/railtie_spec.rb +37 -0
- data/spec/result_spec.rb +12 -0
- data/spec/session/qr_service_spec.rb +42 -0
- data/spec/session/store_spec.rb +21 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/web_adapter_spec.rb +55 -0
- data/spec/whatsapp_notifier_spec.rb +102 -0
- metadata +126 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module WhatsAppNotifier
|
|
2
|
+
class Client
|
|
3
|
+
def initialize(configuration:)
|
|
4
|
+
@configuration = configuration
|
|
5
|
+
@providers = {}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def deliver(to:, body:, metadata: {}, provider: nil, idempotency_key: nil)
|
|
9
|
+
payload = {
|
|
10
|
+
to: to,
|
|
11
|
+
body: body,
|
|
12
|
+
metadata: metadata,
|
|
13
|
+
idempotency_key: idempotency_key
|
|
14
|
+
}
|
|
15
|
+
provider_for(provider || @configuration.provider).deliver(payload)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def deliver_bulk(messages, provider: nil, sleeper: ->(seconds) { sleep(seconds) }, rng: Random.new)
|
|
19
|
+
Bulk::Dispatcher.new(client: self, configuration: @configuration, sleeper: sleeper, rng: rng).deliver(messages, provider: provider)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def scan_qr(metadata: {}, provider: nil)
|
|
23
|
+
provider_for(provider || @configuration.provider).scan_qr(metadata: metadata)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def connection_status(metadata: {}, provider: nil)
|
|
27
|
+
provider_for(provider || @configuration.provider).connection_status(metadata: metadata)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def provider_for(key)
|
|
33
|
+
@providers[key] ||= build_provider(key)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def build_provider(key)
|
|
37
|
+
provider_key = key.to_sym
|
|
38
|
+
raise ConfigurationError, "unknown provider: #{key.inspect}" unless provider_key == :web_automation
|
|
39
|
+
|
|
40
|
+
Providers::WebAutomation.new(configuration: @configuration)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "logger"
|
|
2
|
+
|
|
3
|
+
module WhatsAppNotifier
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :provider, :web_adapter, :web_session_path,
|
|
6
|
+
:bulk_base_delay_seconds, :bulk_jitter_seconds, :bulk_max_recipients,
|
|
7
|
+
:bulk_max_attempts, :bulk_retryable_error_codes, :logger,
|
|
8
|
+
:web_automation_enabled, :warn_on_risky_provider
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@provider = :web_automation
|
|
12
|
+
@web_adapter = WebAdapter.new
|
|
13
|
+
@web_session_path = "tmp/whatsapp_notifier/session.json"
|
|
14
|
+
@bulk_base_delay_seconds = 1.0
|
|
15
|
+
@bulk_jitter_seconds = 0.3
|
|
16
|
+
@bulk_max_recipients = 500
|
|
17
|
+
@bulk_max_attempts = 3
|
|
18
|
+
@bulk_retryable_error_codes = %i[rate_limited network_error temporary_failure].freeze
|
|
19
|
+
@logger = Logger.new($stdout)
|
|
20
|
+
@web_automation_enabled = true
|
|
21
|
+
@warn_on_risky_provider = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def validate!
|
|
25
|
+
raise ConfigurationError, "provider is required" if provider.nil?
|
|
26
|
+
raise ConfigurationError, "only :web_automation provider is supported" unless provider.to_sym == :web_automation
|
|
27
|
+
raise ConfigurationError, "bulk_max_recipients must be positive" if bulk_max_recipients.to_i <= 0
|
|
28
|
+
raise ConfigurationError, "bulk_max_attempts must be positive" if bulk_max_attempts.to_i <= 0
|
|
29
|
+
raise ConfigurationError, "web_adapter must be configured and respond to send_message, fetch_qr_code, connection_status" unless valid_web_adapter?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def valid_web_adapter?
|
|
35
|
+
return false unless web_adapter
|
|
36
|
+
|
|
37
|
+
required_methods = %i[send_message fetch_qr_code connection_status]
|
|
38
|
+
required_methods.all? { |method_name| web_adapter.respond_to?(method_name) }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
module WhatsAppNotifier
|
|
4
|
+
module Doctor
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
DEFAULT_PORT = 3001
|
|
8
|
+
|
|
9
|
+
def run(io: $stdout, env: ENV, app_root: Dir.pwd)
|
|
10
|
+
checks = [
|
|
11
|
+
check_bun,
|
|
12
|
+
check_chromium(env: env),
|
|
13
|
+
check_session_dir(env: env, app_root: app_root),
|
|
14
|
+
check_service_url(env: env)
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
checks.each do |check|
|
|
18
|
+
icon = check[:ok] ? "PASS" : "FAIL"
|
|
19
|
+
io.puts("#{icon}: #{check[:name]}")
|
|
20
|
+
io.puts(" #{check[:message]}")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
failed = checks.reject { |check| check[:ok] }
|
|
24
|
+
return true if failed.empty?
|
|
25
|
+
|
|
26
|
+
io.puts("")
|
|
27
|
+
io.puts("Quick fixes:")
|
|
28
|
+
failed.each { |check| io.puts("- #{check[:fix]}") if check[:fix] }
|
|
29
|
+
false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def session_dir(env: ENV, app_root: Dir.pwd)
|
|
33
|
+
env["WHATSAPP_SESSION_DIR"] || File.expand_path("tmp/whatsapp_notifier/.wwebjs_auth", app_root)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def default_service_url
|
|
37
|
+
"http://127.0.0.1:#{DEFAULT_PORT}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def check_bun
|
|
41
|
+
if system("bun --version > /dev/null 2>&1")
|
|
42
|
+
{ ok: true, name: "Bun installed", message: "bun is available in PATH." }
|
|
43
|
+
else
|
|
44
|
+
{
|
|
45
|
+
ok: false,
|
|
46
|
+
name: "Bun installed",
|
|
47
|
+
message: "bun is missing.",
|
|
48
|
+
fix: "Install Bun from https://bun.sh then rerun `bundle exec whatsapp_notifier doctor`."
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def check_chromium(env: ENV)
|
|
54
|
+
executable = env["PUPPETEER_EXECUTABLE_PATH"]
|
|
55
|
+
if executable && File.executable?(executable)
|
|
56
|
+
return { ok: true, name: "Chromium path", message: "Using PUPPETEER_EXECUTABLE_PATH=#{executable}." }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
common_paths = ["/usr/bin/chromium", "/usr/bin/chromium-browser", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"]
|
|
60
|
+
found = common_paths.find { |path| File.executable?(path) }
|
|
61
|
+
|
|
62
|
+
if found
|
|
63
|
+
{ ok: true, name: "Chromium path", message: "Detected Chromium-compatible executable at #{found}." }
|
|
64
|
+
else
|
|
65
|
+
{
|
|
66
|
+
ok: false,
|
|
67
|
+
name: "Chromium path",
|
|
68
|
+
message: "No Chromium executable found.",
|
|
69
|
+
fix: "Install Chromium/Chrome or set PUPPETEER_EXECUTABLE_PATH=/path/to/chrome."
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def check_session_dir(env: ENV, app_root: Dir.pwd)
|
|
75
|
+
dir = session_dir(env: env, app_root: app_root)
|
|
76
|
+
FileUtils.mkdir_p(dir)
|
|
77
|
+
File.write(File.join(dir, ".write_test"), "ok")
|
|
78
|
+
File.delete(File.join(dir, ".write_test"))
|
|
79
|
+
{ ok: true, name: "Session directory", message: "Writable directory at #{dir}." }
|
|
80
|
+
rescue StandardError => e
|
|
81
|
+
{
|
|
82
|
+
ok: false,
|
|
83
|
+
name: "Session directory",
|
|
84
|
+
message: "Cannot write to #{dir}: #{e.message}",
|
|
85
|
+
fix: "Ensure the directory is writable or set WHATSAPP_SESSION_DIR to a writable path."
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def check_service_url(env: ENV)
|
|
90
|
+
service_url = env["WHATSAPP_NOTIFIER_SERVICE_URL"] || env["WHATSAPP_SERVICE_URL"] || default_service_url
|
|
91
|
+
if service_url.match?(%r{\Ahttps?://})
|
|
92
|
+
{ ok: true, name: "Service URL", message: "Using #{service_url}." }
|
|
93
|
+
else
|
|
94
|
+
{
|
|
95
|
+
ok: false,
|
|
96
|
+
name: "Service URL",
|
|
97
|
+
message: "Invalid URL: #{service_url.inspect}",
|
|
98
|
+
fix: "Set WHATSAPP_NOTIFIER_SERVICE_URL to something like #{default_service_url}."
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module WhatsAppNotifier
|
|
2
|
+
module Jobs
|
|
3
|
+
class SendMessageJob
|
|
4
|
+
def self.perform_later(notification_class_name, params)
|
|
5
|
+
raise LoadError, "ActiveJob is required for deliver_later" unless defined?(::ActiveJob::Base)
|
|
6
|
+
|
|
7
|
+
perform_now(notification_class_name, params)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.perform_now(notification_class_name, params)
|
|
11
|
+
new.perform(notification_class_name, params)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def perform(notification_class_name, params)
|
|
15
|
+
klass = notification_class_name.split("::").inject(Object) { |ctx, const| ctx.const_get(const) }
|
|
16
|
+
klass.with(params).deliver_now
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
module WhatsAppNotifier
|
|
2
|
+
class Notification
|
|
3
|
+
class << self
|
|
4
|
+
attr_accessor :default_to, :default_provider, :default_template
|
|
5
|
+
|
|
6
|
+
def with(params = {})
|
|
7
|
+
new(params)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to(value = nil)
|
|
11
|
+
return default_to if value.nil?
|
|
12
|
+
self.default_to = value
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def provider(value = nil)
|
|
16
|
+
return default_provider if value.nil?
|
|
17
|
+
self.default_provider = value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def template(name = nil, body = nil)
|
|
21
|
+
@templates ||= {}
|
|
22
|
+
return default_template if name.nil?
|
|
23
|
+
|
|
24
|
+
self.default_template = name.to_sym
|
|
25
|
+
@templates[name.to_sym] = body if body
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def templates
|
|
29
|
+
@templates ||= {}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def deliver_now(params = {})
|
|
33
|
+
with(params).deliver_now
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def deliver_later(params = {})
|
|
37
|
+
Jobs::SendMessageJob.perform_later(name, params)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
attr_reader :params
|
|
42
|
+
|
|
43
|
+
def initialize(params = {})
|
|
44
|
+
@params = params
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def message
|
|
48
|
+
return render_template if template_name
|
|
49
|
+
|
|
50
|
+
raise NotImplementedError, "#{self.class.name} must implement #message or define template"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to
|
|
54
|
+
params[:to] || self.class.default_to
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def provider
|
|
58
|
+
params[:provider] || self.class.default_provider
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def metadata
|
|
62
|
+
params[:metadata] || {}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def template_name
|
|
66
|
+
(params[:template] || self.class.default_template)&.to_sym
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def template_params
|
|
70
|
+
params[:params] || {}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def deliver_now
|
|
74
|
+
raise ConfigurationError, "recipient is required" if to.nil? || to.to_s.strip.empty?
|
|
75
|
+
|
|
76
|
+
WhatsAppNotifier.deliver(
|
|
77
|
+
to: to,
|
|
78
|
+
body: message,
|
|
79
|
+
provider: provider,
|
|
80
|
+
metadata: metadata
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def render_template
|
|
87
|
+
body = self.class.templates[template_name]
|
|
88
|
+
raise ConfigurationError, "template not found: #{template_name}" unless body
|
|
89
|
+
|
|
90
|
+
body.gsub(/\{\{(\w+)\}\}/) { |_match| template_params[Regexp.last_match(1).to_sym].to_s }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module WhatsAppNotifier
|
|
2
|
+
module Providers
|
|
3
|
+
class Base
|
|
4
|
+
attr_reader :configuration
|
|
5
|
+
|
|
6
|
+
def initialize(configuration:)
|
|
7
|
+
@configuration = configuration
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def deliver(_payload)
|
|
11
|
+
raise NotImplementedError, "#{self.class.name} must implement #deliver"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def scan_qr(metadata: {})
|
|
15
|
+
raise NotImplementedError, "#{self.class.name} does not support QR scanning"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def connection_status(metadata: {})
|
|
19
|
+
raise NotImplementedError, "#{self.class.name} does not support status checking"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module WhatsAppNotifier
|
|
2
|
+
module Providers
|
|
3
|
+
class WebAutomation < Base
|
|
4
|
+
def initialize(configuration:)
|
|
5
|
+
super
|
|
6
|
+
@store = Session::Store.new(path: configuration.web_session_path)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def deliver(payload)
|
|
10
|
+
raise ConfigurationError, "web automation provider is disabled" unless configuration.web_automation_enabled
|
|
11
|
+
|
|
12
|
+
adapter = configuration.web_adapter
|
|
13
|
+
raise ConfigurationError, "web_adapter must be configured for web_automation provider" unless adapter.respond_to?(:send_message)
|
|
14
|
+
|
|
15
|
+
warn_risk_once
|
|
16
|
+
session = session_for(payload.fetch(:metadata, {}))
|
|
17
|
+
response = adapter.send_message(payload: payload, session: session)
|
|
18
|
+
persist_session(response.fetch(:session, {}), payload.fetch(:metadata, {}))
|
|
19
|
+
|
|
20
|
+
Result.new(
|
|
21
|
+
success: response.fetch(:success),
|
|
22
|
+
provider: :web_automation,
|
|
23
|
+
message_id: response[:message_id],
|
|
24
|
+
error_code: response[:error_code],
|
|
25
|
+
error_message: response[:error_message],
|
|
26
|
+
wait_seconds: response[:wait_seconds],
|
|
27
|
+
metadata: response.fetch(:metadata, {})
|
|
28
|
+
)
|
|
29
|
+
rescue StandardError => e
|
|
30
|
+
Result.new(success: false, provider: :web_automation, error_code: :delivery_exception, error_message: e.message)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def scan_qr(metadata: {})
|
|
34
|
+
raise ConfigurationError, "web automation provider is disabled" unless configuration.web_automation_enabled
|
|
35
|
+
|
|
36
|
+
adapter = configuration.web_adapter
|
|
37
|
+
raise ConfigurationError, "web_adapter must be configured for web_automation provider" unless adapter.respond_to?(:fetch_qr_code)
|
|
38
|
+
|
|
39
|
+
Session::QrService.new(store: @store, adapter: adapter).qr_code(metadata: metadata)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def connection_status(metadata: {})
|
|
43
|
+
raise ConfigurationError, "web automation provider is disabled" unless configuration.web_automation_enabled
|
|
44
|
+
|
|
45
|
+
adapter = configuration.web_adapter
|
|
46
|
+
raise ConfigurationError, "web_adapter must be configured for web_automation provider" unless adapter.respond_to?(:connection_status)
|
|
47
|
+
|
|
48
|
+
adapter.connection_status(metadata: metadata)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def session_for(metadata)
|
|
55
|
+
user_id = metadata[:user_id]
|
|
56
|
+
return @store.load unless user_id
|
|
57
|
+
|
|
58
|
+
all_sessions = @store.load
|
|
59
|
+
all_sessions.fetch(:users, {}).fetch(user_key(user_id), {})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def persist_session(next_session, metadata)
|
|
63
|
+
user_id = metadata[:user_id]
|
|
64
|
+
return @store.save(next_session) unless user_id
|
|
65
|
+
|
|
66
|
+
all_sessions = @store.load
|
|
67
|
+
users = all_sessions.fetch(:users, {})
|
|
68
|
+
users[user_key(user_id)] = next_session
|
|
69
|
+
@store.save(all_sessions.merge(users: users))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def user_key(user_id)
|
|
73
|
+
user_id.to_s.to_sym
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def warn_risk_once
|
|
77
|
+
return unless configuration.warn_on_risky_provider
|
|
78
|
+
return if defined?(@risk_warned) && @risk_warned
|
|
79
|
+
|
|
80
|
+
configuration.logger.warn("web_automation provider uses WhatsApp Web automation. Use responsibly and follow WhatsApp policies.")
|
|
81
|
+
@risk_warned = true
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module WhatsAppNotifier
|
|
2
|
+
class Railtie < ::Rails::Railtie
|
|
3
|
+
config.whatsapp_notifier = {}
|
|
4
|
+
|
|
5
|
+
initializer "whatsapp_notifier.configure" do |app|
|
|
6
|
+
WhatsAppNotifier.configure do |config|
|
|
7
|
+
app.config.whatsapp_notifier.each do |key, value|
|
|
8
|
+
setter = "#{key}="
|
|
9
|
+
config.public_send(setter, value) if config.respond_to?(setter)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module WhatsAppNotifier
|
|
2
|
+
class Result
|
|
3
|
+
attr_reader :success, :provider, :message_id, :error_code, :error_message, :wait_seconds, :metadata
|
|
4
|
+
|
|
5
|
+
def initialize(success:, provider:, message_id: nil, error_code: nil, error_message: nil, wait_seconds: nil, metadata: {})
|
|
6
|
+
@success = success
|
|
7
|
+
@provider = provider
|
|
8
|
+
@message_id = message_id
|
|
9
|
+
@error_code = error_code
|
|
10
|
+
@error_message = error_message
|
|
11
|
+
@wait_seconds = wait_seconds
|
|
12
|
+
@metadata = metadata
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def success?
|
|
16
|
+
success
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def failure?
|
|
20
|
+
!success?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|