hey-you 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.rubocop.yml +0 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +67 -0
- data/README.md +373 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/hey_you.gemspec +33 -0
- data/lib/hey-you.rb +8 -0
- data/lib/hey_you/builder/_base.rb +40 -0
- data/lib/hey_you/builder/email.rb +17 -0
- data/lib/hey_you/builder/push.rb +15 -0
- data/lib/hey_you/builder.rb +51 -0
- data/lib/hey_you/channels/_base.rb +42 -0
- data/lib/hey_you/channels/email.rb +87 -0
- data/lib/hey_you/channels/push.rb +38 -0
- data/lib/hey_you/config/conigurable.rb +24 -0
- data/lib/hey_you/config/email.rb +31 -0
- data/lib/hey_you/config/push.rb +29 -0
- data/lib/hey_you/config.rb +111 -0
- data/lib/hey_you/receiver.rb +89 -0
- data/lib/hey_you/sender.rb +81 -0
- data/lib/hey_you/version.rb +3 -0
- metadata +170 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative '_base'
|
2
|
+
|
3
|
+
module HeyYou
|
4
|
+
class Builder
|
5
|
+
class Email < Base
|
6
|
+
attr_reader :subject, :body, :layout, :mailer_class, :mailer_method, :delivery_method
|
7
|
+
|
8
|
+
def build
|
9
|
+
@mailer_class = ch_data.fetch('mailer_class', nil)
|
10
|
+
@mailer_method = ch_data.fetch('mailer_method', nil)
|
11
|
+
@delivery_method = ch_data.fetch('delivery_method', nil)
|
12
|
+
@body = interpolate(ch_data.fetch('body'), options)
|
13
|
+
@subject = interpolate(ch_data.fetch('subject'), options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative '_base'
|
2
|
+
|
3
|
+
module HeyYou
|
4
|
+
class Builder
|
5
|
+
class Push < Base
|
6
|
+
attr_reader :body, :title, :data
|
7
|
+
|
8
|
+
def build
|
9
|
+
@title = interpolate(ch_data.fetch('title'), options)
|
10
|
+
@body = interpolate(ch_data.fetch('body'), options)
|
11
|
+
@data = options[:data] || {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
require_relative 'builder/email'
|
3
|
+
require_relative 'builder/push'
|
4
|
+
|
5
|
+
module HeyYou
|
6
|
+
class Builder
|
7
|
+
attr_reader :data, :options
|
8
|
+
|
9
|
+
# Load data from collection yaml via key and interpolate variables.
|
10
|
+
# Define methods for each registered channel. After initialize you can use
|
11
|
+
# `instance.<ch_name>`. It will be return instance of HeyYou::Builder::<YOUR_CHANNEL_NAME>
|
12
|
+
#
|
13
|
+
def initialize(key, **options)
|
14
|
+
@data = fetch_from_collection_by_key(key, options[:locale])
|
15
|
+
@options = options
|
16
|
+
config.registered_channels.each do |ch|
|
17
|
+
ch_builder =
|
18
|
+
HeyYou::Builder.const_get("#{ch.downcase.capitalize}").new(data, key, options)
|
19
|
+
instance_variable_set("@#{ch}".to_sym, ch_builder)
|
20
|
+
|
21
|
+
define_ch_method(ch)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def define_ch_method(ch)
|
28
|
+
method_proc = -> { instance_variable_get("@#{ch}".to_sym) }
|
29
|
+
self.class.send(:define_method, ch, method_proc)
|
30
|
+
end
|
31
|
+
|
32
|
+
def fetch_from_collection_by_key(key, locale)
|
33
|
+
keys = []
|
34
|
+
if config.localization
|
35
|
+
locale = locale || I18n.locale
|
36
|
+
raise UnknownLocale, 'You should pass locale.' unless locale
|
37
|
+
keys << locale
|
38
|
+
end
|
39
|
+
keys = keys + key.to_s.split(config.splitter)
|
40
|
+
keys.reduce(config.collection) do |memo, nested_key|
|
41
|
+
memo[nested_key.to_s] if memo
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def config
|
46
|
+
Config.config
|
47
|
+
end
|
48
|
+
|
49
|
+
class UnknownLocale < StandardError; end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module HeyYou
|
2
|
+
module Channels
|
3
|
+
class Base
|
4
|
+
class << self
|
5
|
+
def send!
|
6
|
+
raise NotImplementedError, 'You should define #send! method in your channel.'
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def credentials_present?
|
12
|
+
required_credentials.all? do |cred|
|
13
|
+
if config.send(self.name.split('::').last.downcase).send(cred).nil?
|
14
|
+
config.logger&.info(
|
15
|
+
"[WARN] required credential was not set. " +
|
16
|
+
"Set `config.#{self.name.downcase}.#{cred}` to send notification with #{self.name.downcase}"
|
17
|
+
)
|
18
|
+
return false
|
19
|
+
end
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def required_credentials
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
|
28
|
+
def config
|
29
|
+
Config.config
|
30
|
+
end
|
31
|
+
|
32
|
+
def ch_name
|
33
|
+
self.class.name.split('::').last.downcase
|
34
|
+
end
|
35
|
+
|
36
|
+
def log(msg)
|
37
|
+
config.log("[#{ch_name.upcase}] #{msg}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require_relative '_base'
|
2
|
+
require 'mail'
|
3
|
+
|
4
|
+
module HeyYou
|
5
|
+
module Channels
|
6
|
+
class Email < Base
|
7
|
+
class << self
|
8
|
+
def send!(builder, to:, **options)
|
9
|
+
raise CredentialsNotExists unless credentials_present?
|
10
|
+
|
11
|
+
method = config.email.default_mailing ? :send_via_mail : :send_via_custom_class
|
12
|
+
public_send(method, builder, to, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Send email via custom class instance.
|
16
|
+
def send_via_custom_class(builder, to, **options)
|
17
|
+
mailer = mailer_class_from_builder(builder, options)
|
18
|
+
|
19
|
+
mailer_method = options[:mailer_method] ||
|
20
|
+
builder.email.mailer_method ||
|
21
|
+
config.email.default_mailer_method
|
22
|
+
|
23
|
+
delivery_method = options[:delivery_method] ||
|
24
|
+
builder.email.delivery_method ||
|
25
|
+
config.email.default_delivery_method
|
26
|
+
|
27
|
+
log("Build mail via #{mailer}##{mailer_method}. Delivery with #{delivery_method}")
|
28
|
+
mailer_msg = mailer.public_send(mailer_method, data: builder.email, to: to)
|
29
|
+
mailer_msg.public_send(delivery_method)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Send email with standard mail (gem 'mail')
|
33
|
+
def send_via_mail(builder, to, **_)
|
34
|
+
context = self
|
35
|
+
mail = Mail.new do
|
36
|
+
from HeyYou::Config.instance.email.from
|
37
|
+
to to
|
38
|
+
subject builder.email.subject
|
39
|
+
body context.get_body(builder.email.body)
|
40
|
+
end
|
41
|
+
|
42
|
+
mail.delivery_method config.email.mail_delivery_method
|
43
|
+
log("Send mail #{mail}")
|
44
|
+
mail.deliver
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_body(body_text)
|
48
|
+
# TODO: Load layout from config here.
|
49
|
+
body_text
|
50
|
+
end
|
51
|
+
|
52
|
+
def required_credentials
|
53
|
+
%i[from]
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def mailer_class_from_builder(builder, **options)
|
59
|
+
mailer_class = options[:mailer_class] ||
|
60
|
+
builder.email.mailer_class ||
|
61
|
+
config.email.default_mailer_class
|
62
|
+
unless mailer_class
|
63
|
+
raise(
|
64
|
+
MailerClassNotDefined,
|
65
|
+
'You must set mailer_class in notifications collection or pass :mailer_class option'
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
begin
|
70
|
+
mailer = Object.const_get(mailer_class.to_s)
|
71
|
+
rescue NameError
|
72
|
+
raise ActionMailerClassNotDefined, "Mailer #{mailer_class} not initialized"
|
73
|
+
end
|
74
|
+
|
75
|
+
mailer
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class CredentialsNotExists < StandardError;
|
80
|
+
end
|
81
|
+
class ActionMailerClassNotDefined < StandardError;
|
82
|
+
end
|
83
|
+
class MailerClassNotDefined < StandardError;
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative '_base'
|
2
|
+
|
3
|
+
module HeyYou
|
4
|
+
module Channels
|
5
|
+
class Push < Base
|
6
|
+
class << self
|
7
|
+
def send!(builder, to:, **options)
|
8
|
+
options = build_options(builder)
|
9
|
+
config.logger&.info("[PUSH] Send #{options} body for #{ids(to)}")
|
10
|
+
config.push.fcm_client.send(ids(to), options)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def build_options(builder)
|
16
|
+
{
|
17
|
+
data: builder.push.data,
|
18
|
+
notification: {
|
19
|
+
title: builder.push.title,
|
20
|
+
body: builder.push.body
|
21
|
+
},
|
22
|
+
priority: builder.options[:priority] || config.push.priority,
|
23
|
+
time_to_live: config.push.ttl
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def ids(to)
|
28
|
+
return to if to.is_a?(Array)
|
29
|
+
[to]
|
30
|
+
end
|
31
|
+
|
32
|
+
def required_credentials
|
33
|
+
%i[fcm_token]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module HeyYou
|
4
|
+
class Config
|
5
|
+
module Configurable
|
6
|
+
|
7
|
+
def self.extended klass
|
8
|
+
klass.class_eval do
|
9
|
+
include Singleton
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def configure(&block)
|
14
|
+
# TODO: log warn "Already configured" instead nil
|
15
|
+
@configured ? nil : instance_eval(&block)
|
16
|
+
@configured = true
|
17
|
+
end
|
18
|
+
|
19
|
+
def config
|
20
|
+
@config ||= self.instance
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'conigurable'
|
2
|
+
require 'mail'
|
3
|
+
|
4
|
+
module HeyYou
|
5
|
+
class Config
|
6
|
+
class Email
|
7
|
+
extend Configurable
|
8
|
+
|
9
|
+
MAIL_DELIVERY_METHOD = :sendmail
|
10
|
+
DEFAULT_ACTION_MAILER_METHOD = :send!
|
11
|
+
DEFAULT_DELIVERY_METHOD = :deliver_now
|
12
|
+
|
13
|
+
attr_accessor(
|
14
|
+
:from,
|
15
|
+
:mail_delivery_method,
|
16
|
+
:default_mailing,
|
17
|
+
:default_delivery_method,
|
18
|
+
:default_mailer_class,
|
19
|
+
:default_mailer_method
|
20
|
+
)
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@mail_delivery_method ||= MAIL_DELIVERY_METHOD
|
24
|
+
@default_mailing ||= !default_mailer_class.nil?
|
25
|
+
@async ||= true
|
26
|
+
@default_mailer_method ||= DEFAULT_ACTION_MAILER_METHOD
|
27
|
+
@default_delivery_method ||= DEFAULT_DELIVERY_METHOD
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'conigurable'
|
2
|
+
require 'fcm'
|
3
|
+
|
4
|
+
module HeyYou
|
5
|
+
class Config
|
6
|
+
class Push
|
7
|
+
extend Configurable
|
8
|
+
|
9
|
+
DEFAULT_PRIORITY = 'high'
|
10
|
+
DEFAULT_TTL = 60
|
11
|
+
DEFAULT_FCM_TIMEOUT = 30
|
12
|
+
|
13
|
+
attr_accessor :fcm_token, :priority, :ttl, :fcm_timeout
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@priority = DEFAULT_PRIORITY
|
17
|
+
@ttl = DEFAULT_TTL
|
18
|
+
@fcm_timeout = DEFAULT_FCM_TIMEOUT
|
19
|
+
end
|
20
|
+
|
21
|
+
def fcm_client
|
22
|
+
raise FcmTokenNotExists, 'Can\'t create fcm client: fcm_token not exists' unless fcm_token
|
23
|
+
@fcm_client ||= FCM.new(fcm_token, timeout: fcm_timeout)
|
24
|
+
end
|
25
|
+
|
26
|
+
class FcmTokenNotExists < StandardError; end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require_relative 'config/conigurable'
|
3
|
+
require_relative 'config/push'
|
4
|
+
require_relative 'config/email'
|
5
|
+
|
6
|
+
#
|
7
|
+
# @config REQUIRED collection_file [String] - File path for general notifications file
|
8
|
+
# @config OPTIONAL env_collection_file [String] - File path for environment notifications file
|
9
|
+
# @config OPTIONAL splitter [String] - Chars for split notifications keys in builder.
|
10
|
+
# For example:
|
11
|
+
# if splitter eq `.` you can pass to notification builder 'key_1.nested_key'
|
12
|
+
# if splitter eq `/` you can pass to notification builder 'key_1/nested_key'
|
13
|
+
# @config OPTIONAL registered_channels - Channels available for service. If your application
|
14
|
+
# planning use only push and email just set it as [:push, :email]. Default all channels
|
15
|
+
# will available.
|
16
|
+
module HeyYou
|
17
|
+
class Config
|
18
|
+
extend Configurable
|
19
|
+
|
20
|
+
DEFAULT_REGISTERED_CHANNELS = %i[email push]
|
21
|
+
DEFAULT_SPLITTER = '.'
|
22
|
+
DEFAULT_GLOBAL_LOG_TAG = 'HeyYou'
|
23
|
+
DEFAULT_LOCALIZATION_FLAG = false
|
24
|
+
|
25
|
+
class CollectionFileNotDefined < StandardError; end
|
26
|
+
|
27
|
+
attr_reader :collection, :env_collection, :configured, :registered_receivers
|
28
|
+
attr_accessor(
|
29
|
+
:collection_files, :env_collection_file, :splitter,
|
30
|
+
:registered_channels, :localization, :logger, :log_tag
|
31
|
+
)
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@registered_channels ||= DEFAULT_REGISTERED_CHANNELS
|
35
|
+
@splitter ||= DEFAULT_SPLITTER
|
36
|
+
@registered_receivers = []
|
37
|
+
@log_tag ||= DEFAULT_GLOBAL_LOG_TAG
|
38
|
+
@localization ||= DEFAULT_LOCALIZATION_FLAG
|
39
|
+
define_ch_config_methods
|
40
|
+
end
|
41
|
+
|
42
|
+
def collection_file
|
43
|
+
@collection_files || raise(
|
44
|
+
CollectionFileNotDefined,
|
45
|
+
'You must define HeyYou::Config.collection_files'
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def collection
|
50
|
+
@collection ||= load_collection
|
51
|
+
end
|
52
|
+
|
53
|
+
def env_collection
|
54
|
+
@env_collection ||= load_env_collection
|
55
|
+
end
|
56
|
+
|
57
|
+
def registrate_receiver(receiver_class)
|
58
|
+
log("#{receiver_class} registrated as receiver")
|
59
|
+
@registered_receivers << receiver_class
|
60
|
+
end
|
61
|
+
|
62
|
+
# Registrate new custom channel.
|
63
|
+
# For successful registration, in application must be exists:
|
64
|
+
# 1. HeyYou::Channels::<YOUR_CHANNEL_NAME> < HeyYou::Channels::Base
|
65
|
+
# 2. HeyYou::Builder::<YOUR_CHANNEL_NAME> < HeyYou::Builder::Base
|
66
|
+
# 3. HeyYou::Config::<YOUR_CHANNEL_NAME> extended HeyYou::Config::Configurable
|
67
|
+
#
|
68
|
+
def registrate_channel(ch)
|
69
|
+
registered_channels << ch.to_sym
|
70
|
+
define_ch_config_method(ch)
|
71
|
+
end
|
72
|
+
|
73
|
+
def log(msg)
|
74
|
+
logger&.info("[#{log_tag}] #{msg} ")
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def define_ch_config_methods
|
80
|
+
registered_channels.each do |ch|
|
81
|
+
define_ch_config_method(ch)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Define method for fetch config of channel.
|
86
|
+
#
|
87
|
+
# For example, if ch == 'push' will define method #push for class instance.
|
88
|
+
# New method will return instance of channel config instance
|
89
|
+
def define_ch_config_method(ch)
|
90
|
+
method_proc = -> { self.class.const_get(ch.capitalize).config }
|
91
|
+
self.class.send(:define_method, ch, method_proc)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Load yaml from collection_file and merge it with yaml from env_collection_file
|
95
|
+
def load_collection
|
96
|
+
@collection_files = [collection_files] if collection_files.is_a?(String)
|
97
|
+
notification_collection = {}
|
98
|
+
collection_files.each do |file|
|
99
|
+
notification_collection.merge!(YAML.load_file(file))
|
100
|
+
end
|
101
|
+
notification_collection.merge!(env_collection)
|
102
|
+
end
|
103
|
+
|
104
|
+
def load_env_collection
|
105
|
+
if env_collection_file
|
106
|
+
return YAML.load_file(env_collection_file) rescue { }
|
107
|
+
end
|
108
|
+
{}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative 'sender'
|
2
|
+
|
3
|
+
module HeyYou
|
4
|
+
module Receiver
|
5
|
+
attr_reader :receiver_channels, :receiver_data
|
6
|
+
|
7
|
+
def self.extended klass
|
8
|
+
klass.class_eval do
|
9
|
+
# Method will be injected for instances methods of class.
|
10
|
+
#
|
11
|
+
# This can use something like: user.send_notification('key', options).
|
12
|
+
def send_notification(notification_key, **options)
|
13
|
+
Sender.send_to(self, notification_key, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Registrate class as receiver.
|
19
|
+
# In parameters pass hash where keys - channels names, and values -
|
20
|
+
# procs with values for receive.
|
21
|
+
#
|
22
|
+
# For instances of classes will be created methods `#{channel_name}_ch_receive_info`
|
23
|
+
# after registrate your class as receiver.
|
24
|
+
#
|
25
|
+
# Example:
|
26
|
+
#
|
27
|
+
# class User
|
28
|
+
# extend HeyYou::Receiver
|
29
|
+
#
|
30
|
+
# receive(
|
31
|
+
# push: -> { push_tokens.value },
|
32
|
+
# email: -> { priority_email }
|
33
|
+
# )
|
34
|
+
#
|
35
|
+
# ## Or you can use receive with options:
|
36
|
+
# receive(
|
37
|
+
# email: { subject: -> { priority_email }, options: { mailer_class: UserMailer, mailer_method: :notify } }
|
38
|
+
# )
|
39
|
+
#
|
40
|
+
# ...
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# user = User.new( push_tokens: PushToken.new(value: "456"), priority_email: 'example@mail.com')
|
44
|
+
# user.push_ch_receive_info # => 456
|
45
|
+
#
|
46
|
+
def receive(receiver_data)
|
47
|
+
check_channels(receiver_data.keys)
|
48
|
+
|
49
|
+
@receiver_data = receiver_data
|
50
|
+
@receiver_channels = receiver_data.keys
|
51
|
+
hey_you_config.registrate_receiver(self)
|
52
|
+
|
53
|
+
define_receive_info_methods
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def check_channels(channels)
|
59
|
+
channels.all? do |ch|
|
60
|
+
next if hey_you_config.registered_channels.include?(ch.to_sym)
|
61
|
+
raise(
|
62
|
+
NotRegisteredChannel,
|
63
|
+
"Channel #{ch} not registered. Registered channels: #{hey_you_config.registered_channels}"
|
64
|
+
)
|
65
|
+
end
|
66
|
+
@received_channels = channels
|
67
|
+
end
|
68
|
+
|
69
|
+
def define_receive_info_methods
|
70
|
+
receiver_channels.each do |ch|
|
71
|
+
if receiver_data[ch].is_a?(Hash)
|
72
|
+
me = self
|
73
|
+
self.send(:define_method, "#{ch}_ch_receive_info", receiver_data[ch].fetch(:subject))
|
74
|
+
self.send(:define_method, "#{ch}_ch_receive_options", -> { me.receiver_data[ch].fetch(:options, {}) })
|
75
|
+
else
|
76
|
+
self.send(:define_method, "#{ch}_ch_receive_info", receiver_data[ch])
|
77
|
+
self.send(:define_method, "#{ch}_ch_receive_options", -> { {} })
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def hey_you_config
|
83
|
+
Config.config
|
84
|
+
end
|
85
|
+
|
86
|
+
class NotRegisteredChannel < StandardError;
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'builder'
|
2
|
+
|
3
|
+
require_relative 'channels/push'
|
4
|
+
require_relative 'channels/email'
|
5
|
+
|
6
|
+
module HeyYou
|
7
|
+
class Sender
|
8
|
+
class << self
|
9
|
+
# Send notifications for receiver
|
10
|
+
#
|
11
|
+
# @input receiver [Instance of receiver class] - registrated receiver
|
12
|
+
# @input notification_key [String] - key for notification builder
|
13
|
+
# @input options [Hash]
|
14
|
+
# @option only [String/Array[String]] - whitelist for using channels
|
15
|
+
#
|
16
|
+
def send_to(receiver, notification_key, **options)
|
17
|
+
unless receiver_valid?(receiver)
|
18
|
+
raise NotRegisteredReceiver, "Class '#{receiver.class}' not registered as receiver"
|
19
|
+
end
|
20
|
+
|
21
|
+
result = send!(notification_key, receiver, **options)
|
22
|
+
config.log("Sender result: #{result}")
|
23
|
+
result
|
24
|
+
end
|
25
|
+
|
26
|
+
def send!(notification_key, receiver, **options)
|
27
|
+
to_hash = {}
|
28
|
+
receiver.class.receiver_channels.each do |ch|
|
29
|
+
to_hash[ch] = {
|
30
|
+
# Fetch receiver's info for sending: phone_number, email, etc
|
31
|
+
subject: receiver.public_send("#{ch}_ch_receive_info"),
|
32
|
+
# Fetch receiver's options like :mailer_class
|
33
|
+
options: receiver.public_send("#{ch}_ch_receive_options") || {}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
builder = Builder.new(notification_key, options)
|
38
|
+
response = {}
|
39
|
+
config.registered_channels.each do |ch|
|
40
|
+
if channel_allowed?(ch, to_hash, builder, options)
|
41
|
+
config.log(
|
42
|
+
"Send #{ch} to #{to_hash[ch][:subject]} with data: #{builder.send(ch).data}" \
|
43
|
+
" and options: #{to_hash[ch][:options]}"
|
44
|
+
)
|
45
|
+
response[ch] = Channels.const_get(ch.to_s.capitalize).send!(
|
46
|
+
builder, to: to_hash[ch][:subject], **to_hash[ch][:options]
|
47
|
+
)
|
48
|
+
else
|
49
|
+
config.log("Channel #{ch} not allowed.")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
response
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def channel_allowed?(ch, to, builder, **options)
|
58
|
+
unless to[ch].is_a?(Hash) ? to[ch.to_sym][:subject] || to[ch.to_s][:subject] : to[ch.to_sym] || to[ch.to_s]
|
59
|
+
return false
|
60
|
+
end
|
61
|
+
channel_allowed_by_only?(ch, options[:only]) && !builder.send(ch).nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
def receiver_valid?(receiver)
|
65
|
+
config.registered_receivers.include?(receiver.class)
|
66
|
+
end
|
67
|
+
|
68
|
+
def channel_allowed_by_only?(ch, only)
|
69
|
+
return true unless only
|
70
|
+
return only.map(&:to_sym).include?(ch.to_sym) if only.is_a?(Array)
|
71
|
+
only.to_sym == ch.to_sym
|
72
|
+
end
|
73
|
+
|
74
|
+
def config
|
75
|
+
Config.config
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class NotRegisteredReceiver < StandardError; end
|
80
|
+
end
|
81
|
+
end
|