hominid 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,85 @@
1
+ module Hominid
2
+ class Base
3
+
4
+ # MailChimp API Documentation: http://www.mailchimp.com/api/1.2/
5
+ MAILCHIMP_API_VERSION = "1.2"
6
+
7
+ def initialize(config = {})
8
+ if defined?(Rails.root) && (!config || config.empty?)
9
+ config = YAML.load(File.open("#{Rails.root}/config/hominid.yml"))[Rails.env].symbolize_keys
10
+ end
11
+ api_endpoint = config[:api_key].split('-').last
12
+ config.merge(:username => config[:username].to_s, :password => config[:password].to_s)
13
+ defaults = {:send_welcome => false,
14
+ :double_opt_in => false,
15
+ :update_existing => true,
16
+ :replace_interests => true,
17
+ :merge_tags => {}}
18
+ @config = defaults.merge(config).freeze
19
+ @chimpApi = XMLRPC::Client.new2("http://#{api_endpoint}.api.mailchimp.com/#{MAILCHIMP_API_VERSION}/")
20
+ end
21
+
22
+ # Security related methods
23
+ # --------------------------------
24
+
25
+ def add_api_key
26
+ @chimpApi.call("apikeyAdd", *@config.values_at(:username, :password, :api_key))
27
+ end
28
+
29
+ def expire_api_key
30
+ @chimpApi.call("apikeyExpire", *@config.values_at(:username, :password, :api_key))
31
+ end
32
+
33
+ def api_keys(include_expired = false)
34
+ username, password = *@config.values_at(:username, :password)
35
+ @chimpApi.call("apikeys", username, password, include_expired)
36
+ end
37
+
38
+ # Used internally by Hominid
39
+ # --------------------------------
40
+
41
+ # handle common cases for which the Mailchimp API would raise Exceptions
42
+ def clean_merge_tags(merge_tags)
43
+ return {} unless merge_tags.is_a? Hash
44
+ merge_tags.each do |key, value|
45
+ if merge_tags[key].is_a? String
46
+ merge_tags[key] = value.gsub("\v", '')
47
+ elsif merge_tags[key].nil?
48
+ merge_tags[key] = ''
49
+ end
50
+ end
51
+ end
52
+
53
+ def apply_defaults_to(options)
54
+ @config.merge(options)
55
+ end
56
+
57
+ def call(method, *args)
58
+ @chimpApi.call(method, @config[:api_key], *args)
59
+ rescue XMLRPC::FaultException => error
60
+ case error.faultCode
61
+ when 230
62
+ raise AlreadySubscribed.new(error)
63
+ when 231
64
+ raise AlreadyUnsubscribed.new(error)
65
+ when 232
66
+ raise NotExists.new(error)
67
+ when 233, 215
68
+ raise NotSubscribed.new(error)
69
+ else
70
+ raise HominidError.new(error)
71
+ end
72
+ rescue RuntimeError => error
73
+ if error.message =~ /Wrong type NilClass\. Not allowed!/
74
+ hashes = args.select{|a| a.is_a? Hash}
75
+ errors = hashes.select{|k, v| v.nil? }.collect{ |k, v| "#{k} is Nil." }.join(' ')
76
+ raise CommunicationError.new(errors)
77
+ else
78
+ raise error
79
+ end
80
+ rescue Exception => error
81
+ raise CommunicationError.new(error.message)
82
+ end
83
+ end
84
+ end
85
+
@@ -0,0 +1,172 @@
1
+ module Hominid
2
+
3
+ class Campaign < Base
4
+
5
+ # Campaign related methods
6
+ # --------------------------------
7
+
8
+ attr_reader :campaign_id
9
+ attr_reader :attributes
10
+
11
+ def initialize(*args)
12
+ options = args.last.is_a?(Hash) ? args.last : {}
13
+ raise StandardError.new('Please provide a Campaign ID.') unless options[:id]
14
+ @campaign_id = options.delete(:id)
15
+ @attributes = options.delete(:attributes)
16
+ super(options)
17
+ end
18
+
19
+ def self.all
20
+ # Get all campaigns for this mailchimp account
21
+ new(:id => 0).call("campaigns").to_a.collect { |c| Campaign.new(:id => c.delete('id'), :attributes => c) }
22
+ end
23
+
24
+ def self.find_by_list_name(list_name)
25
+ new(:id => 0).call("campaigns", {:list_id => List.find_by_name(list_name).list_id}).to_a.collect { |c| Campaign.new(:id=> c.delete('id'), :attributes => c) }
26
+ end
27
+
28
+ def self.find_by_list_id(list_id)
29
+ # Find all campaigns for the given list
30
+ new(:id => 0).call("campaigns", {:list_id => list_id}).to_a.collect { |c| Campaign.new(:id=> c.delete('id'), :attributes => c) }
31
+ end
32
+
33
+ def self.find_by_title(title)
34
+ # Find campaign by title
35
+ all.find { |c| c.attributes['title'] =~ /#{title}/ }
36
+ end
37
+
38
+ def self.find_by_type(type)
39
+ # Find campaign by type. Possible choices are:
40
+ # 'regular'
41
+ # 'plaintext'
42
+ # 'absplit'
43
+ # 'rss'
44
+ # 'inspection'
45
+ # 'trans'
46
+ # 'auto'
47
+ all.find { |campaign| campaign.attributes['type'] =~ /#{type}/ }
48
+ end
49
+
50
+ def self.find_by_web_id(web_id)
51
+ # Find campaigns by web_id
52
+ all.find { |campaign| campaign.attributes['web_id'] =~ /#{web_id}/ }
53
+ end
54
+
55
+ def self.find_by_id(id)
56
+ # Find campaign by id
57
+ all.find { |campaign| (campaign.campaign_id == id.to_s) }
58
+ end
59
+
60
+ def self.find(id_or_web_id)
61
+ # Campaign finder method
62
+ all = self.all
63
+ campaign = self.find_by_id(id_or_web_id.to_s).to_a + self.find_by_web_id(id_or_web_id.to_i).to_a
64
+ return campaign.blank? ? nil : campaign.first
65
+ end
66
+
67
+ def self.create(type = 'regular', options = {}, content = {}, segment_options = {}, type_opts = {})
68
+ # Create a new campaign
69
+ # The options hash should be structured as follows:
70
+ #
71
+ # :list_id = (string) The ID of the list to send this campaign to.
72
+ # :subject = (string) The subject of the campaign.
73
+ # :from_email = (string) The email address this campaign will come from.
74
+ # :from_name = (string) The name that this campaign will come from.
75
+ # :to_email = (string) The To: name recipients will see.
76
+ # :template_id = (integer) The ID of the template to use for this campaign (optional).
77
+ # :folder_id = (integer) The ID of the folder to file this campaign in (optional).
78
+ # :tracking = (array) What to track for this campaign (optional).
79
+ # :title = (string) Internal title for this campaign (optional).
80
+ # :authenticate = (boolean) Set to true to authenticate campaign (optional).
81
+ # :analytics = (array) Google analytics tags (optional).
82
+ # :auto_footer = (boolean) Auto-generate the footer (optional)?
83
+ # :inline_css = (boolean) Inline the CSS styles (optional)?
84
+ # :generate_text = (boolean) Auto-generate text from HTML email (optional)?
85
+ #
86
+ # Visit http://www.mailchimp.com/api/1.2/campaigncreate.func.php for more information about creating
87
+ # campaigns via the API.
88
+ #
89
+ new(:id => 0).call("campaignCreate", type, options, content, segment_options, type_opts)
90
+ ## TODO: Return the new campaign with the ID returned from the API
91
+ end
92
+
93
+ def self.templates
94
+ # Get the templates for this account
95
+ new(:id => 0).call("campaignTemplates")
96
+ end
97
+
98
+ def add_order(order)
99
+ # Attach Ecommerce Order Information to a campaign.
100
+ # The order hash should be structured as follows:
101
+ #
102
+ # :id = (string) the order id
103
+ # :email_id = (string) email id of the subscriber (mc_eid query string)
104
+ # :total = (double) Show only campaigns with this from_name.
105
+ # :shipping = (string) *optional - the total paid for shipping fees.
106
+ # :tax = (string) *optional - the total tax paid.
107
+ # :store_id = (string) a unique id for the store sending the order in
108
+ # :store_name = (string) *optional - A readable name for the store, typicaly the hostname.
109
+ # :plugin_id = (string) the MailChimp-assigned Plugin Id. Using 1214 for the moment.
110
+ # :items = (array) the individual line items for an order, using the following keys:
111
+ #
112
+ # :line_num = (integer) *optional - line number of the item on the order
113
+ # :product_id = (integer) internal product id
114
+ # :product_name = (string) the name for the product_id associated with the item
115
+ # :category_id = (integer) internal id for the (main) category associated with product
116
+ # :category_name = (string) the category name for the category id
117
+ # :qty = (double) the quantity of items ordered
118
+ # :cost = (double) the cost of a single item (i.e., not the extended cost of the line)
119
+ order.merge(:campaign_id => @campaign_id)
120
+ call("campaignEcommAddOrder", order)
121
+ end
122
+
123
+ def campaign_stats()
124
+ # Get the stats of a campaign
125
+ call("campaignStats", @campaign_id)
126
+ end
127
+
128
+ # Get the HTML & text content for a campaign
129
+ # :for_archive = (boolean) default true, true returns the content as it would appear in the archive, false returns the raw HTML/text
130
+ def campaign_content(for_archive = true)
131
+ # Get the content of a campaign
132
+ call("campaignContent", @campaign_id, for_archive)
133
+ end
134
+
135
+ def delete_campaign()
136
+ # Delete a campaign
137
+ call("campaignDelete", @campaign_id)
138
+ end
139
+
140
+ def replicate_campaign()
141
+ # Replicate a campaign (returns ID of new campaign)
142
+ call("campaignReplicate", @campaign_id)
143
+ end
144
+
145
+ def schedule_campaign(time = "#{1.day.from_now}")
146
+ # Schedule a campaign
147
+ ## TODO: Add support for A/B Split scheduling
148
+ call("campaignSchedule", @campaign_id, time)
149
+ end
150
+
151
+ def send_now()
152
+ # Send a campaign
153
+ call("campaignSendNow", @campaign_id)
154
+ end
155
+
156
+ # Send a test of a campaign
157
+ def send_test(emails = {})
158
+ call("campaignSendTest", @campaign_id, emails)
159
+ end
160
+
161
+ def update(name, value)
162
+ # Update a campaign
163
+ call("campaignUpdate", @campaign_id, name, value)
164
+ end
165
+
166
+ def unschedule()
167
+ # Unschedule a campaign
168
+ call("campaignUnschedule", @campaign_id)
169
+ end
170
+ end
171
+ end
172
+
@@ -0,0 +1,45 @@
1
+ module Hominid
2
+
3
+ class Helper < Base
4
+
5
+ # Helper methods
6
+ # --------------------------------
7
+
8
+ def self.account_details
9
+ # Get details for this account.
10
+ new.call("getAccountDetails")
11
+ end
12
+
13
+ def self.convert_css_to_inline(html, strip_css = false)
14
+ # Convert CSS styles to inline styles and (optionally) remove original styles
15
+ new.call("inlineCss", html, strip_css)
16
+ end
17
+
18
+ def self.create_folder(name)
19
+ # Create a new folder to file campaigns in
20
+ new.call("createFolder", name)
21
+ end
22
+
23
+ def self.generate_text(type, content)
24
+ # Have HTML content auto-converted to a text-only format.
25
+ # The options for text type are:
26
+ # 'html' => Expects a string of HTML(default).
27
+ # 'template' => Expects an array.
28
+ # 'url' => Expects a valid and public URL.
29
+ # 'cid' => Expects a campaign ID.
30
+ # 'tid' => Expects a template ID.
31
+ new.call("generateText", type, content)
32
+ end
33
+
34
+ def self.html_to_text(content)
35
+ # Convert HTML content to text
36
+ new.call("generateText", 'html', content)
37
+ end
38
+
39
+ def self.ping
40
+ # Ping the Mailchimp API
41
+ new.call("ping")
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,150 @@
1
+ module Hominid
2
+
3
+ class List < Base
4
+
5
+ # List related methods
6
+ # --------------------------------
7
+
8
+ attr_reader :list_id
9
+ attr_reader :attributes
10
+
11
+ def initialize(*args)
12
+ options = args.last.is_a?(Hash) ? args.last : {}
13
+ raise StandardError.new('Please provide a List ID.') unless options[:id]
14
+ @list_id = options.delete(:id)
15
+ @attributes = options.delete(:attributes)
16
+ super(options)
17
+ end
18
+
19
+ def self.all
20
+ # Get all lists for this mailchimp account
21
+ new(:id => 0).call("lists").to_a.collect { |list| List.new(:id => list.delete('id'), :attributes => list) }
22
+ end
23
+
24
+ def self.find_by_name(name)
25
+ # Find list by name
26
+ all.find { |list| list.attributes['name'] =~ /#{name}/ }
27
+ end
28
+
29
+ def self.find_by_web_id(web_id)
30
+ # Find list by name
31
+ all.find { |list| (list.attributes['web_id'] == web_id.to_i) }
32
+ end
33
+
34
+ def self.find_by_id(id)
35
+ # Find list by id
36
+ all.find { |list| (list.list_id == id.to_s) }
37
+ end
38
+
39
+ def self.find(id_or_web_id)
40
+ # List finder method
41
+ all = self.all
42
+ list = self.find_by_id(id_or_web_id.to_s).to_a + self.find_by_web_id(id_or_web_id.to_i).to_a
43
+ return list.blank? ? nil : list.first
44
+ end
45
+
46
+ def create_group(group)
47
+ # Add an interest group to a list
48
+ call("listInterestGroupAdd", @list_id, group)
49
+ end
50
+ alias :interest_group_add :create_group
51
+
52
+ def create_tag(tag, name, required = false)
53
+ # Add a merge tag to a list
54
+ call("listMergeVarAdd", @list_id, tag, name, required)
55
+ end
56
+ alias :merge_var_add :create_tag
57
+
58
+ def delete_group(group)
59
+ # Delete an interest group for a list
60
+ call("listInterestGroupDel", @list_id, group)
61
+ end
62
+ alias :interest_group_del :delete_group
63
+
64
+ def delete_tag(tag)
65
+ # Delete a merge tag and all its members
66
+ call("listMergeVarDel", @list_id, tag)
67
+ end
68
+ alias :merge_var_del :delete_tag
69
+
70
+ def groups()
71
+ # Get the interest groups for a list
72
+ call("listInterestGroups", @list_id)
73
+ end
74
+ alias :interest_groups :groups
75
+
76
+
77
+ def member_info(email)
78
+ # Get a member of a list
79
+ call("listMemberInfo", @list_id, email)
80
+ end
81
+
82
+ def members(status = "subscribed", since = "2000-01-01 00:00:00", start = 0, limit = 100)
83
+ # Get members of a list based on status
84
+ # Select members based on one of the following statuses:
85
+ # 'subscribed'
86
+ # 'unsubscribed'
87
+ # 'cleaned'
88
+ # 'updated'
89
+ #
90
+ # Select members that have updated their status or profile by providing
91
+ # a "since" date in the format of YYYY-MM-DD HH:MM:SS
92
+ call("listMembers", @list_id, status, since, start, limit)
93
+ end
94
+
95
+ def merge_tags()
96
+ # Get the merge tags for a list
97
+ call("listMergeVars", @list_id)
98
+ end
99
+ alias :merge_vars :merge_tags
100
+
101
+ def subscribe(email, options = {})
102
+ # Subscribe a member to this list
103
+ merge_tags = clean_merge_tags options[:merge_tags]
104
+ options = apply_defaults_to({:email_type => "html"}.merge(options))
105
+ call(
106
+ "listSubscribe",
107
+ @list_id,
108
+ email,
109
+ merge_tags,
110
+ *options.values_at(
111
+ :email_type,
112
+ :double_opt_in,
113
+ :update_existing,
114
+ :replace_interests,
115
+ :send_welcome
116
+ )
117
+ )
118
+ end
119
+
120
+ def subscribe_many(subscribers, options = {})
121
+ # Subscribe an array of email addresses
122
+ # subscribers(array) = [{:EMAIL => 'example@email.com', :EMAIL_TYPE => 'html'}]
123
+ subscribers = subscribers.collect { |subscriber| clean_merge_tags(subscriber) }
124
+ options = apply_defaults_to({:update_existing => true}.merge(options))
125
+ call("listBatchSubscribe", @list_id, subscribers, *options.values_at(:double_opt_in, :update_existing, :replace_interests))
126
+ end
127
+ alias :batch_subscribe :subscribe_many
128
+
129
+ def unsubscribe(current_email, options = {})
130
+ # Unsubscribe a list member
131
+ options = apply_defaults_to({:delete_member => true}.merge(options))
132
+ call("listUnsubscribe", @list_id, current_email, *options.values_at(:delete_member, :send_goodbye, :send_notify))
133
+ end
134
+
135
+ def unsubscribe_many(emails, options = {})
136
+ # Unsubscribe an array of email addresses
137
+ # emails(array) = ['first@email.com', 'second@email.com']
138
+ options = apply_defaults_to({:delete_member => true}.merge(options))
139
+ call("listBatchUnsubscribe", @list_id, emails, *options.values_at(:delete_member, :send_goodbye, :send_notify))
140
+ end
141
+ alias :batch_unsubscribe :unsubscribe_many
142
+
143
+ def update_member(current_email, merge_tags = {}, email_type = "html")
144
+ # Update a member of this list
145
+ call("listUpdateMember", @list_id, current_email, merge_tags, email_type, true)
146
+ end
147
+
148
+ end
149
+ end
150
+