hermes-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f9071b7e4a8721889e58bd6c4c197b19435190a6
4
+ data.tar.gz: e812e7eb39709a33c11cf6b78f20aced40b5fc58
5
+ SHA512:
6
+ metadata.gz: 34d64a8a92cdad335b5b4bdb9660f178c4d98163065209102befe8a86299116b14dd59a83254b202b4d7d4ebbf2cb9c7191316cbfd03a04dffdf34775ecdc9e0
7
+ data.tar.gz: e3fa78be6f7ec3b0c703d06e7e871ca2048c4c584aee63403c5c20f201310e5bae1ecf2a8e07b9365c28d813c3353a3f95c1e2448297bbb8ece0ef97ddc7e0d8
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Dogwood Labs, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ = MailgunRails
2
+
3
+ This project rocks and uses MIT-LICENSE.
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'hermes'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+
32
+ task default: :test
@@ -0,0 +1,16 @@
1
+ require 'action_mailer'
2
+ require 'json'
3
+ require 'httparty'
4
+
5
+ # all of the Hermes support files
6
+ Dir[File.dirname(__FILE__) + '/support/*.rb'].each {|file| require file }
7
+ Dir[File.dirname(__FILE__) + '/hermes/*.rb'].each {|file| require file }
8
+
9
+ # all of the generic (abstract) provider support files
10
+ Dir[File.dirname(__FILE__) + '/providers/*.rb'].each {|file| require file }
11
+
12
+ # all of the actual provider support files
13
+ Dir[File.dirname(__FILE__) + '/providers/**/*.rb'].each {|file| require file }
14
+
15
+ module Hermes
16
+ end
@@ -0,0 +1,121 @@
1
+ require 'digest/md5'
2
+
3
+ module Hermes
4
+ class Deliverer
5
+ include Extractors
6
+
7
+ attr_reader :providers, :config
8
+
9
+ def initialize(settings)
10
+ @providers = {}
11
+
12
+ @config = settings[:config]
13
+
14
+ # this will most likely come back as [:email, :sms, :tweet, :webhook]
15
+ provider_types = @config.keys.reject{|key| key == :config}
16
+
17
+ # loop through each and construct a workable array
18
+ provider_types.each do |provider_type|
19
+ @providers[provider_type] ||= []
20
+ providers = settings[provider_type]
21
+ next unless providers.try(:any?)
22
+
23
+ # go through all of the providers and initialize each
24
+ providers.each do |provider_name, options|
25
+ # check to see that the provider class exists
26
+ provider_proper_name = "#{provider_name}_provider".camelize.to_sym
27
+ raise(ProviderNotFoundError, "Could not find provider class Hermes::#{provider_proper_name}") unless Hermes.constants.include?(provider_proper_name)
28
+
29
+ # initialize the provider with the given weight, defaults, and credentials
30
+ provider = Hermes.const_get(provider_proper_name).new(self, options)
31
+ @providers[provider_type] << provider
32
+ end
33
+
34
+ # make sure the provider type has an aggregate weight of more than 1
35
+ aweight = aggregate_weight_for_type(provider_type)
36
+ unless aweight > 0
37
+ raise(InvalidWeightError, "Provider type:#{provider_type} has aggregate weight:#{aweight}")
38
+ end
39
+ end
40
+ end
41
+
42
+ def test_mode?
43
+ !!@config[:test]
44
+ end
45
+
46
+ def handle_success(provider_name)
47
+ @config[:stats]
48
+ end
49
+
50
+ def handle_failure(provider_name, exception)
51
+
52
+ end
53
+
54
+ def should_deliver?
55
+ !self.test_mode? && ActionMailer::Base.perform_deliveries
56
+ end
57
+
58
+ def aggregate_weight_for_type(type)
59
+ providers = @providers[type]
60
+ return 0 if providers.empty?
61
+
62
+ providers.map(&:weight).inject(0, :+)
63
+ end
64
+
65
+ def weighted_provider_for_type(type)
66
+ providers = @providers[type]
67
+ return nil if providers.empty?
68
+
69
+ # get the aggregate weight, and do a rand based on it
70
+ random_index = rand(aggregate_weight_for_type(type))
71
+ # puts "random_index:#{random_index}"
72
+
73
+ # loop through each, exclusive range, and find the one that it falls on
74
+ running_total = 0
75
+ providers.each do |provider|
76
+ # puts "running_total:#{running_total}"
77
+ left_index = running_total
78
+ right_index = running_total + provider.weight
79
+ # puts "left_index:#{left_index} right_index:#{right_index}"
80
+
81
+ if (left_index...right_index).include?(random_index)
82
+ return provider
83
+ else
84
+ running_total += provider.weight
85
+ end
86
+ end
87
+ end
88
+
89
+ def delivery_type_for(rails_message)
90
+ to = extract_to(rails_message, format: :address)
91
+
92
+ if to.is_a?(Hash) && to[:twitter_username]
93
+ :tweet
94
+ elsif rails_message.to.first.include?('@')
95
+ :email
96
+ elsif to.is_a?(Phone)
97
+ :sms
98
+ elsif to.is_a?(URI)
99
+ :webhook
100
+ else
101
+ raise UnknownDeliveryTypeError, "Cannot determine provider type from provided to:#{to}"
102
+ end
103
+ end
104
+
105
+ def deliver!(rails_message)
106
+ # figure out what we're delivering
107
+ delivery_type = delivery_type_for(rails_message)
108
+
109
+ # set this on the message so it's available throughout
110
+ rails_message.hermes_type = delivery_type
111
+
112
+ # find a provider, weight matters here
113
+ provider = weighted_provider_for_type(delivery_type)
114
+
115
+ # and then send the message
116
+ provider.send_message(rails_message)
117
+ end
118
+ end
119
+ end
120
+
121
+ ActionMailer::Base.add_delivery_method :hermes, Hermes::Deliverer
@@ -0,0 +1,13 @@
1
+ module Hermes
2
+ # if provider is listed in settings, but no provider class is available
3
+ class ProviderNotFoundError < StandardError; end
4
+
5
+ # thrown if provider weight goes below 1
6
+ class InvalidWeightError < StandardError; end
7
+
8
+ # thrown if configuration does not provide all required credentials
9
+ class InsufficientCredentialsError < StandardError; end
10
+
11
+ # thrown if deliverer cannot figure out what type of provider to use for provided rails message
12
+ class UnknownDeliveryTypeError < StandardError; end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :hermes_type
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Hermes
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :mailgun_variables
4
+ attr_accessor :mailgun_options
5
+ attr_accessor :mailgun_recipient_variables
6
+ attr_accessor :mailgun_headers
7
+ attr_accessor :mailgun_domain
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ module Hermes
2
+ class MailgunAttachment < StringIO
3
+ attr_reader :original_filename, :content_type, :path
4
+
5
+ def initialize (attachment, *rest)
6
+ @path = ''
7
+ if rest.detect {|opt| opt[:inline] }
8
+ basename = @original_filename = attachment.cid
9
+ else
10
+ basename = @original_filename = attachment.filename
11
+ end
12
+ @content_type = attachment.content_type.split(';')[0]
13
+ super attachment.body.decoded
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,42 @@
1
+ module Hermes
2
+ class MailgunProvider < Provider
3
+ required_credentials :api_key
4
+
5
+ def send_message(rails_message)
6
+ domain = rails_message.mailgun_domain || self.defaults[:domain]
7
+ message = self.mailgun_message(rails_message)
8
+
9
+ if self.deliverer.should_deliver?
10
+ self.client.send_message(domain, message)
11
+ end
12
+
13
+ self.message_success(rails_message)
14
+ end
15
+
16
+ def mailgun_message(rails_message)
17
+ message = Mailgun::MessageBuilder.new
18
+
19
+ # basics
20
+ message.set_from_address(extract_from(rails_message))
21
+ message.add_recipient(:to, extract_to(rails_message))
22
+ message.set_subject(rails_message[:subject])
23
+ message.set_html_body(extract_html(rails_message))
24
+ message.set_text_body(extract_text(rails_message))
25
+ message.set_message_id(rails_message.message_id)
26
+
27
+ # optionals
28
+ message.set_from_address('h:reply-to', rails_message[:reply_to].formatted.first) if rails_message[:reply_to]
29
+
30
+ # and any attachments
31
+ rails_message.attachments.try(:each) do |attachment|
32
+ message.add_attachment(Hermes::EmailAttachment.new(attachment))
33
+ end
34
+
35
+ return message
36
+ end
37
+
38
+ def client
39
+ Mailgun::Client.new(self.credentials[:api_key])
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,30 @@
1
+ module Hermes
2
+ class OutboundWebhookProvider < Provider
3
+
4
+ def send_message(rails_message)
5
+ payload = payload(rails_message)
6
+ outbound_webhook = OutboundWebhook.create!(payload)
7
+ rails_message[:message_id] = outbound_webhook.id
8
+
9
+ byebug
10
+
11
+ if self.deliverer.should_deliver?
12
+ outbound_webhook.deliver_async
13
+ end
14
+
15
+ self.message_success(rails_message)
16
+ rescue Exception => e
17
+ self.message_failure(rails_message, e)
18
+ end
19
+
20
+ def payload(rails_message)
21
+ {
22
+ :endpoint => extract_to(rails_message),
23
+ :headers => {
24
+ 'Content-Type' => 'application/json'
25
+ },
26
+ :body => extract_text(rails_message)
27
+ }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :plivo_url
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ module Hermes
2
+ class PlivoProvider < Provider
3
+ required_credentials :auth_id, :auth_token
4
+
5
+ def send_message(rails_message)
6
+ payload = payload(rails_message)
7
+
8
+ if self.deliverer.should_deliver?
9
+ result = self.client.send_message(payload)
10
+ rails_message[:message_id] = result["api_id"]
11
+ else
12
+ # rails message still needs a fake sid as if it succeeded
13
+ rails_message[:message_id] = SecureRandom.uuid
14
+ end
15
+
16
+ self.message_success(rails_message)
17
+ end
18
+
19
+ def payload(rails_message)
20
+ {
21
+ src: extract_from(rails_message).full_number,
22
+ dst: extract_to(rails_message),
23
+ text: extract_text(rails_message),
24
+ type: :sms,
25
+ url: rails_message.plivo_url || self.defaults[:url]
26
+ }
27
+ end
28
+
29
+ def client
30
+ Plivo::RestAPI.new(self.credentials[:auth_id], self.credentials[:auth_token])
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,58 @@
1
+ module Hermes
2
+ class Provider
3
+ include Extractors
4
+
5
+ class << self
6
+ attr_accessor :_required_credentials
7
+
8
+ def required_credentials(*args)
9
+ self._required_credentials = args.to_a
10
+ end
11
+ end
12
+
13
+ attr_reader :deliverer, :defaults, :credentials, :weight
14
+
15
+ def initialize(deliverer, options = {})
16
+ options.symbolize_keys!
17
+
18
+ @deliverer = deliverer
19
+ @defaults = options[:defaults]
20
+ @credentials = (options[:credentials] || {}).symbolize_keys
21
+ @weight = options[:weight].to_i
22
+
23
+ if self.class._required_credentials.try(:any?)
24
+ # provider defines required credentials, let's make sure to check we have everything we need
25
+ if !((@credentials.keys & self.class._required_credentials) == self.class._required_credentials)
26
+ # we're missing something, raise here for hard failure
27
+ raise(InsufficientCredentialsError, "Credentials passed:#{@credentials.keys} do not satisfy all required:#{self.class._required_credentials}")
28
+ end
29
+ end
30
+
31
+ unless @weight >= 0
32
+ # provider weights need to be 0 (disabled), or greater than 0 to show as active
33
+ raise(InvalidWeightError, "Provider name:#{provider_name} has invalid weight:#{@weight}")
34
+ end
35
+ end
36
+
37
+ def provider_name
38
+ self.class.name.demodulize.underscore.gsub('_provider', '')
39
+ end
40
+
41
+ def send_message(rails_message)
42
+ raise "this is an abstract method and must be defined in the subclass"
43
+ end
44
+
45
+ def message_success(rails_message)
46
+ ActionMailer::Base.deliveries << rails_message if self.deliverer.test_mode?
47
+ self.deliverer.handle_success(self.class.name)
48
+ end
49
+
50
+ def message_failure(rails_message, exception)
51
+ Utils.log_and_puts "--- MESSAGE SEND FAILURE ---"
52
+ Utils.log_and_puts exception.message
53
+ Utils.log_and_puts exception.backtrace.join("\n")
54
+ Utils.log_and_puts "--- MESSAGE SEND FAILURE ---"
55
+ self.deliverer.handle_failure(self.class)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,46 @@
1
+ module Hermes
2
+ class SendgridProvider < Provider
3
+ required_credentials :api_user, :api_key
4
+
5
+ def send_message(rails_message)
6
+ payload = payload(rails_message)
7
+
8
+ if self.deliverer.should_deliver?
9
+ client.send(payload)
10
+ end
11
+
12
+ self.message_success(message_success)
13
+ end
14
+
15
+ def payload(rails_message)
16
+ # requireds
17
+ message = SendGrid::Mail.new({
18
+ from: extract_from(rails_message, :address),
19
+ from_name: extract_from(rails_message, :name),
20
+ to: extract_to(rails_message, :address),
21
+ to_name: extract_to(rails_message, :name),
22
+ subject: rails_message.subject,
23
+ html: extract_html(rails_message),
24
+ text: extract_text(rails_message),
25
+ })
26
+
27
+ # optionals
28
+ message.reply_to = rails_message[:reply_to].formatted.first if rails_message[:reply_to]
29
+ message.message_id = rails_message[:message_id].value if rails_message.message_id
30
+
31
+ # and any attachments
32
+ rails_message.attachments.try(:each) do |attachment|
33
+ message.add_attachment_file(Hermes::EmailAttachment.new(attachment))
34
+ end
35
+
36
+ return message
37
+ end
38
+
39
+ def client
40
+ SendGrid::Client.new({
41
+ api_user: self.credentials[:api_user],
42
+ api_key: self.credentials[:api_key]
43
+ })
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :twilio_status_callback
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ module Hermes
2
+ class TwilioProvider < Provider
3
+ required_credentials :account_sid, :auth_token
4
+
5
+ def send_message(rails_message)
6
+ payload = payload(rails_message)
7
+
8
+ if self.deliverer.should_deliver?
9
+ result = self.client.account.messages.create(payload)
10
+
11
+ # set the sid onto the rails message as the message id, used for tracking
12
+ rails_message[:message_id] = result.sid
13
+ else
14
+ # rails message still needs a fake sid as if it succeeded
15
+ rails_message[:message_id] = SecureRandom.uuid
16
+ end
17
+
18
+ self.message_success(rails_message)
19
+ rescue Exception => e
20
+ self.message_failure(rails_message, e)
21
+ end
22
+
23
+ def payload(rails_message)
24
+ payload = {
25
+ to: extract_to(rails_message).full_number,
26
+ from: extract_from(rails_message),
27
+ body: extract_text(rails_message),
28
+ }
29
+
30
+ payload[:status_callback] = rails_message.twilio_status_callback if rails_message.twilio_status_callback
31
+
32
+ return payload
33
+ end
34
+
35
+ def client
36
+ Twilio::REST::Client.new(self.credentials[:account_sid], self.credentials[:auth_token])
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,6 @@
1
+ module Mail
2
+ class Message
3
+ attr_accessor :twitter_oauth_token
4
+ attr_accessor :twitter_oauth_token_secret
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module Hermes
2
+ class TwitterProvider < Provider
3
+ required_credentials :consumer_key, :consumer_secret
4
+
5
+ def send_message(rails_message)
6
+ self.client(rails_message).update(extract_text(rails_message))
7
+ end
8
+
9
+ def client(rails_message)
10
+ # this will already be an instance of Twitter::client
11
+ client = extract_to(rails_message)
12
+
13
+ # just need to set the consumer key and secret and
14
+ # then we'll be ready for liftoff
15
+ client.consumer_key = self.credentials[:consumer_key]
16
+ client.consumer_secret = self.credentials[:consumer_secret]
17
+
18
+ return client
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module Hermes
2
+ class B64Y
3
+ def self.encode(payload)
4
+ Base64.strict_encode64(YAML.dump(payload))
5
+ end
6
+
7
+ def self.decode(payload)
8
+ YAML.load(Base64.strict_decode64(payload))
9
+ rescue Exception => e
10
+ Utils.log_and_puts "--- DECODE FAILURE ---"
11
+ Utils.log_and_puts payload
12
+ Utils.log_and_puts "--- DECODE FAILURE ---"
13
+ raise e
14
+ end
15
+
16
+ def self.decodable?(payload)
17
+ # check to make sure when we decode that it's going to look like a YAML object
18
+ Base64.strict_decode64(payload)[0..2] == '---'
19
+ rescue
20
+ false
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module Hermes
2
+ class EmailAttachment < StringIO
3
+ attr_reader :original_filename, :content_type, :path
4
+
5
+ def initialize (attachment, *rest)
6
+ @path = ''
7
+ if rest.detect {|opt| opt[:inline] }
8
+ basename = @original_filename = attachment.cid
9
+ else
10
+ basename = @original_filename = attachment.filename
11
+ end
12
+ @content_type = attachment.content_type.split(';')[0]
13
+ super attachment.body.decoded
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,68 @@
1
+ module Hermes
2
+ module Extractors
3
+ # @see http://stackoverflow.com/questions/4868205/rails-mail-getting-the-body-as-plain-text
4
+ def extract_html(rails_message)
5
+ if rails_message.html_part
6
+ rails_message.html_part.body.decoded
7
+ else
8
+ rails_message.content_type =~ /text\/html/ ? rails_message.body.decoded : nil
9
+ end
10
+ end
11
+
12
+ def extract_text(rails_message)
13
+ if rails_message.multipart?
14
+ rails_message.text_part ? rails_message.text_part.body.decoded.strip : nil
15
+ else
16
+ rails_message.content_type =~ /text\/plain/ ? rails_message.body.decoded.strip : nil
17
+ end
18
+ end
19
+
20
+ # format can be full|name|address
21
+ def extract_from(rails_message, format: :full)
22
+ from = complex_extract(rails_message.from.first)
23
+ return from[:value] if from[:decoded]
24
+
25
+ case format
26
+ when :full
27
+ rails_message[:from].formatted.first
28
+ when :name
29
+ rails_message[:from].address_list.addresses.first.name
30
+ when :address
31
+ rails_message[:from].address_list.addresses.first.address
32
+ end
33
+ end
34
+
35
+ # format can be full|name|address
36
+ def extract_to(rails_message, format: :full)
37
+ to = complex_extract(rails_message.to.first)
38
+ return to[:value] if to[:decoded]
39
+
40
+ case format
41
+ when :full
42
+ rails_message[:to].formatted.first
43
+ when :name
44
+ rails_message[:to].address_list.addresses.first.name
45
+ when :address
46
+ rails_message[:to].address_list.addresses.first.address
47
+ end
48
+ end
49
+
50
+ # when passing in to/from addresses that are complex objects
51
+ # like a Hash or Twitter::Client instance, they will be YAMLed
52
+ # and then Base64ed since Mail::Message really only wants
53
+ # to play with strings for these fields
54
+ def complex_extract(address_container)
55
+ if B64Y.decodable?(address_container)
56
+ {
57
+ decoded: true,
58
+ value: B64Y.decode(address_container)
59
+ }
60
+ else
61
+ {
62
+ decoded: false,
63
+ value: address_container
64
+ }
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,235 @@
1
+ module Hermes
2
+ class Phone
3
+
4
+ attr_accessor :country, :number
5
+
6
+ @@countries = {
7
+ :af => ['+93', 'Afghanistan'],
8
+ :al => ['+355', 'Albania'],
9
+ :dz => ['+213', 'Algeria'],
10
+ :as => ['+1684', 'American Samoa', :ws],
11
+ :ad => ['+376', 'Andorra'],
12
+ :ao => ['+244', 'Angola'],
13
+ :ai => ['+1264', 'Anguilla'],
14
+ :ag => ['+1268', 'Antigua and Barbuda'],
15
+ :ar => ['+54', 'Argentina'],
16
+ :am => ['+374', 'Armenia'],
17
+ :aw => ['+297', 'Aruba'],
18
+ :au => ['+61', 'Australia/Cocos/Christmas Island'],
19
+ :at => ['+43', 'Austria'],
20
+ :az => ['+994', 'Azerbaijan'],
21
+ :bs => ['+1', 'Bahamas'],
22
+ :bh => ['+973', 'Bahrain'],
23
+ :bd => ['+880', 'Bangladesh'],
24
+ :bb => ['+1246', 'Barbados'],
25
+ :by => ['+375', 'Belarus'],
26
+ :be => ['+32', 'Belgium'],
27
+ :bz => ['+501', 'Belize'],
28
+ :bj => ['+229', 'Benin'],
29
+ :bm => ['+1441', 'Bermuda'],
30
+ :bo => ['+591', 'Bolivia'],
31
+ :ba => ['+387', 'Bosnia and Herzegovina'],
32
+ :bw => ['+267', 'Botswana'],
33
+ :br => ['+55', 'Brazil'],
34
+ :bn => ['+673', 'Brunei'],
35
+ :bg => ['+359', 'Bulgaria'],
36
+ :bf => ['+226', 'Burkina Faso'],
37
+ :bi => ['+257', 'Burundi'],
38
+ :kh => ['+855', 'Cambodia'],
39
+ :cm => ['+237', 'Cameroon'],
40
+ :ca => ['+1', 'Canada'],
41
+ :cv => ['+238', 'Cape Verde'],
42
+ :ky => ['+1345', 'Cayman Islands'],
43
+ :cf => ['+236', 'Central Africa'],
44
+ :td => ['+235', 'Chad'],
45
+ :cl => ['+56', 'Chile'],
46
+ :cn => ['+86', 'China'],
47
+ :co => ['+57', 'Colombia'],
48
+ :km => ['+269', 'Comoros'],
49
+ :cg => ['+242', 'Congo'],
50
+ :cd => ['+243', 'Congo, Dem Rep'],
51
+ :cr => ['+506', 'Costa Rica'],
52
+ :hr => ['+385', 'Croatia'],
53
+ :cy => ['+357', 'Cyprus'],
54
+ :cz => ['+420', 'Czech Republic'],
55
+ :dk => ['+45', 'Denmark'],
56
+ :dj => ['+253', 'Djibouti'],
57
+ :dm => ['+1767', 'Dominica'],
58
+ :do => ['+1809', 'Dominican Republic'],
59
+ :eg => ['+20', 'Egypt'],
60
+ :sv => ['+503', 'El Salvador'],
61
+ :gq => ['+240', 'Equatorial Guinea'],
62
+ :ee => ['+372', 'Estonia'],
63
+ :et => ['+251', 'Ethiopia'],
64
+ :fo => ['+298', 'Faroe Islands'],
65
+ :fj => ['+679', 'Fiji'],
66
+ :fi => ['+358', 'Finland/Aland Islands'],
67
+ :fr => ['+33', 'France'],
68
+ :gf => ['+594', 'French Guiana'],
69
+ :pf => ['+689', 'French Polynesia'],
70
+ :ga => ['+241', 'Gabon'],
71
+ :gm => ['+220', 'Gambia'],
72
+ :ge => ['+995', 'Georgia'],
73
+ :de => ['+49', 'Germany'],
74
+ :gh => ['+233', 'Ghana'],
75
+ :gi => ['+350', 'Gibraltar'],
76
+ :gr => ['+30', 'Greece'],
77
+ :gl => ['+299', 'Greenland'],
78
+ :gd => ['+1473', 'Grenada'],
79
+ :gp => ['+590', 'Guadeloupe'],
80
+ :gu => ['+1671', 'Guam'],
81
+ :gt => ['+502', 'Guatemala'],
82
+ :gn => ['+224', 'Guinea'],
83
+ :gy => ['+592', 'Guyana'],
84
+ :ht => ['+509', 'Haiti'],
85
+ :hn => ['+504', 'Honduras'],
86
+ :hk => ['+852', 'Hong Kong'],
87
+ :hu => ['+36', 'Hungary'],
88
+ :is => ['+354', 'Iceland'],
89
+ :in => ['+91', 'India'],
90
+ :id => ['+62', 'Indonesia'],
91
+ :ir => ['+98', 'Iran'],
92
+ :iq => ['+964', 'Iraq'],
93
+ :ie => ['+353', 'Ireland'],
94
+ :il => ['+972', 'Israel'],
95
+ :it => ['+39', 'Italy'],
96
+ :jm => ['+1876', 'Jamaica'],
97
+ :jp => ['+81', 'Japan'],
98
+ :jo => ['+962', 'Jordan'],
99
+ :ke => ['+254', 'Kenya'],
100
+ :kr => ['+82', 'Korea, Republic of'],
101
+ :kw => ['+965', 'Kuwait'],
102
+ :kg => ['+996', 'Kyrgyzstan'],
103
+ :la => ['+856', 'Laos'],
104
+ :lv => ['+371', 'Latvia'],
105
+ :lb => ['+961', 'Lebanon'],
106
+ :ls => ['+266', 'Lesotho'],
107
+ :lr => ['+231', 'Liberia'],
108
+ :ly => ['+218', 'Libya'],
109
+ :li => ['+423', 'Liechtenstein'],
110
+ :lt => ['+370', 'Lithuania'],
111
+ :lu => ['+352', 'Luxembourg'],
112
+ :mo => ['+853', 'Macao'],
113
+ :mk => ['+389', 'Macedonia'],
114
+ :mg => ['+261', 'Madagascar'],
115
+ :mw => ['+265', 'Malawi'],
116
+ :my => ['+60', 'Malaysia'],
117
+ :mv => ['+960', 'Maldives'],
118
+ :ml => ['+223', 'Mali'],
119
+ :mt => ['+356', 'Malta'],
120
+ :mq => ['+596', 'Martinique'],
121
+ :mr => ['+222', 'Mauritania'],
122
+ :mu => ['+230', 'Mauritius'],
123
+ :mx => ['+52', 'Mexico'],
124
+ :mc => ['+377', 'Monaco'],
125
+ :mn => ['+976', 'Mongolia'],
126
+ :me => ['+382', 'Montenegro'],
127
+ :ms => ['+1664', 'Montserrat'],
128
+ :ma => ['+212', 'Morocco/Western Sahara'],
129
+ :mz => ['+258', 'Mozambique'],
130
+ :na => ['+264', 'Namibia'],
131
+ :np => ['+977', 'Nepal'],
132
+ :nl => ['+31', 'Netherlands'],
133
+ :nz => ['+64', 'New Zealand'],
134
+ :ni => ['+505', 'Nicaragua'],
135
+ :ne => ['+227', 'Niger'],
136
+ :ng => ['+234', 'Nigeria'],
137
+ :no => ['+47', 'Norway'],
138
+ :om => ['+968', 'Oman'],
139
+ :pk => ['+92', 'Pakistan'],
140
+ :ps => ['+970', 'Palestinian Territory'],
141
+ :pa => ['+507', 'Panama'],
142
+ :py => ['+595', 'Paraguay'],
143
+ :pe => ['+51', 'Peru'],
144
+ :ph => ['+63', 'Philippines'],
145
+ :pl => ['+48', 'Poland'],
146
+ :pt => ['+351', 'Portugal'],
147
+ :pr => ['+1787', 'Puerto Rico'],
148
+ :qa => ['+974', 'Qatar'],
149
+ :re => ['+262', 'Reunion/Mayotte'],
150
+ :ro => ['+40', 'Romania'],
151
+ :ru => ['+7', 'Russia/Kazakhstan'],
152
+ :rw => ['+250', 'Rwanda'],
153
+ :ws => ['+685', 'Samoa'],
154
+ :sm => ['+378', 'San Marino'],
155
+ :sa => ['+966', 'Saudi Arabia'],
156
+ :sn => ['+221', 'Senegal'],
157
+ :rs => ['+381', 'Serbia'],
158
+ :sc => ['+248', 'Seychelles'],
159
+ :sl => ['+232', 'Sierra Leone'],
160
+ :sg => ['+65', 'Singapore'],
161
+ :sk => ['+421', 'Slovakia'],
162
+ :si => ['+386', 'Slovenia'],
163
+ :za => ['+27', 'South Africa'],
164
+ :es => ['+34', 'Spain'],
165
+ :lk => ['+94', 'Sri Lanka'],
166
+ :kn => ['+1869', 'St Kitts and Nevis'],
167
+ :lc => ['+1758', 'St Lucia'],
168
+ :vc => ['+1784', 'St Vincent Grenadines'],
169
+ :sd => ['+249', 'Sudan'],
170
+ :sr => ['+597', 'Suriname'],
171
+ :sz => ['+268', 'Swaziland'],
172
+ :se => ['+46', 'Sweden'],
173
+ :ch => ['+41', 'Switzerland'],
174
+ :sy => ['+963', 'Syria'],
175
+ :tw => ['+886', 'Taiwan'],
176
+ :tj => ['+992', 'Tajikistan'],
177
+ :tz => ['+255', 'Tanzania'],
178
+ :th => ['+66', 'Thailand'],
179
+ :tg => ['+228', 'Togo'],
180
+ :to => ['+676', 'Tonga'],
181
+ :tt => ['+1868', 'Trinidad and Tobago'],
182
+ :tn => ['+216', 'Tunisia'],
183
+ :tr => ['+90', 'Turkey'],
184
+ :tc => ['+1649', 'Turks and Caicos Islands'],
185
+ :ug => ['+256', 'Uganda'],
186
+ :ua => ['+380', 'Ukraine'],
187
+ :ae => ['+971', 'United Arab Emirates'],
188
+ :gb => ['+44', 'United Kingdom'],
189
+ :us => ['+1', 'United States'],
190
+ :uy => ['+598', 'Uruguay'],
191
+ :uz => ['+998', 'Uzbekistan'],
192
+ :ve => ['+58', 'Venezuela'],
193
+ :vn => ['+84', 'Vietnam'],
194
+ :vg => ['+1284', 'Virgin Islands, British'],
195
+ :vi => ['+1340', 'Virgin Islands, U.S.'],
196
+ :ye => ['+967', 'Yemen'],
197
+ :zm => ['+260', 'Zambia'],
198
+ :zw => ['+263', 'Zimbabwe']
199
+ }.with_indifferent_access
200
+
201
+ def self.countries
202
+ @@countries
203
+ end
204
+
205
+ def ==(other_phone)
206
+ self.country == other_phone.country && self.number == other_phone.number
207
+ end
208
+
209
+ def to_s
210
+ self.full_number
211
+ end
212
+
213
+ def initialize(country, number)
214
+ @country = country
215
+ @number = number
216
+ end
217
+
218
+ def country_prefix
219
+ self.class.prefix_for_country(@country)
220
+ end
221
+
222
+ def country_name
223
+ self.class.name_for_country(@country)
224
+ end
225
+
226
+ def full_number
227
+ self.class.prefix_for_country(self.country) + self.number
228
+ end
229
+
230
+ def self.prefix_for_country(country)
231
+ return @@countries[country.to_s.downcase][0] unless @@countries[country.to_s.downcase].nil? || @@countries[country.to_s.downcase][0].nil?
232
+ return nil
233
+ end
234
+ end
235
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hermes-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Scott Klein
8
+ - Tyler Davis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-04-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 4.0.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 4.0.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: httparty
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0.12'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0.12'
42
+ - !ruby/object:Gem::Dependency
43
+ name: sqlite3
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ description: Rails Action Mailer adapter for balancing multiple providers across email,
57
+ SMS, and webhook
58
+ email:
59
+ - scott@statuspage.io
60
+ - tyler@statuspage.io
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - MIT-LICENSE
66
+ - README.rdoc
67
+ - Rakefile
68
+ - lib/hermes.rb
69
+ - lib/hermes/deliverer.rb
70
+ - lib/hermes/exceptions.rb
71
+ - lib/hermes/mail_ext.rb
72
+ - lib/hermes/version.rb
73
+ - lib/providers/mailgun/mail_ext.rb
74
+ - lib/providers/mailgun/mailgun_attachment.rb
75
+ - lib/providers/mailgun/mailgun_provider.rb
76
+ - lib/providers/outbound_webhook/outbound_webhook_provider.rb
77
+ - lib/providers/plivo/mail_ext.rb
78
+ - lib/providers/plivo/plivo_provider.rb
79
+ - lib/providers/provider.rb
80
+ - lib/providers/sendgrid/sendgrid_provider.rb
81
+ - lib/providers/twilio/mail_ext.rb
82
+ - lib/providers/twilio/twilio_provider.rb
83
+ - lib/providers/twitter/mail_ext.rb
84
+ - lib/providers/twitter/twitter_provider.rb
85
+ - lib/support/b64y.rb
86
+ - lib/support/email_attachment.rb
87
+ - lib/support/extractors.rb
88
+ - lib/support/phone.rb
89
+ homepage: https://github.com/StatusPage/hermes
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.2
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Rails Action Mailer adapter for balancing multiple providers across email,
113
+ SMS, and webhook
114
+ test_files: []