mc 0.0.4

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: 4bd1c554cd8c091423285cbf144266f33c0a7de0
4
+ data.tar.gz: 0286850e7e743ad88f8110826db32c6afc409e2d
5
+ SHA512:
6
+ metadata.gz: 67d67a25c42f884348459c71137266625e8391d1ccec6c1a8e20fb94c3e198850cf225b1ba5d8ed00f616e970806ce36d36b2260655b5c825fce00a3a5b9ba5f
7
+ data.tar.gz: 61e68457d561413a6d54d73569840c17908ba7993ef02d46c814a3b0e9e431f3825c7b3797a2e5ca889e398f6746eb164ebf67157b2d0d852c9635be7d36527b
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ results.html
3
+ /tmp
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'gli', '>= 2.8'
5
+ gem 'gibbon', '1.0.4'
6
+ gem 'filecache'
7
+ gem 'json'
8
+
9
+ gem 'awesome_print'
10
+ gem 'table_print'
11
+ gem 'colorize'
12
+
13
+ gem 'webmock'
@@ -0,0 +1,90 @@
1
+ # mc - MailChimp from the command-line
2
+ With your MailChimp [api key](http://admin.mailchimp.com/account/api) and this gem, you can access a lot of the functionality that you can do through the MailChimp web interface, but all from the command-line. It uses a command/sub-command approach that allows you to quickly find the command you need.
3
+
4
+ Caching is setup so that you can run the command multiple times without worrying about hitting the MailChimp servers. Caching is based on the api call + parameters used and expires in 24 hours. You can fetch new values by using the --skipcache global option.
5
+
6
+ ## Install
7
+ ```sh
8
+ gem install mc
9
+ ```
10
+
11
+ ## Usage
12
+ To see all the commands you can run:
13
+ ```
14
+ mc --help
15
+ ```
16
+ or just:
17
+
18
+ ```
19
+ mc
20
+ ```
21
+
22
+
23
+ You should see something similar to:
24
+
25
+ NAME
26
+ mc - Command line interface to MailChimp. Eep eep!
27
+
28
+ SYNOPSIS
29
+ mc [global options] command [command options] [arguments...]
30
+
31
+ GLOBAL OPTIONS
32
+ --apikey=apikey - MailChimp API Key (default: none)
33
+ --debug - Turn on debugging
34
+ --help - Show this message
35
+ --list=listID - List to use (default: none)
36
+ --output, -o format - formatted, raw, or awesome (default: none)
37
+ --skipcache - Do not use cached result
38
+ --version - Display the program version
39
+
40
+ COMMANDS
41
+ campaigns - Campaign related tasks
42
+ ecomm - Ecomm related actions
43
+ export - Export
44
+ gallery - Manage images and documents that are in your account
45
+ help - Shows a list of commands or help for one command
46
+ helper - Basic admin funtions
47
+ initconfig - Initialize the config file using current global options
48
+ lists - View information about lists and subscribers
49
+ reports - Reports
50
+ search - Search campaigns and members
51
+ templates - Manage templates within your account
52
+ users - Users
53
+ vip - VIPs
54
+
55
+
56
+ You can get details on specific subcommands by:
57
+
58
+ ```
59
+ mc help list
60
+ ```
61
+
62
+ ## With Great Power
63
+ With this tool you'll be able to access your account in ways that will bypass confirmations and limits that you'll find within the regular MailChimp web application. So for example, you can unsubscribe users and send campaigns without any conformation using the correct command and parameters. Caching, as mentioned above, is setup to avoid hitting your account too much, but care still needs to be taken. Lastly, before using this tool it is highly recommended that you read the [MailChimp API faq](http://apidocs.mailchimp.com/api/faq/) and especially note the 'best practices' listed within it.
64
+
65
+
66
+ ## Config File
67
+ To create a config file at ~/.mailchimp you can run:
68
+
69
+ mc initconfig
70
+
71
+ You can then edit that file and include any defaults you want to set. I would highly recommend at a minimal including the api key and a default list:
72
+
73
+ :apikey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-usx
74
+ :default_list: xxxxxxxxxx
75
+
76
+ > Note: once you get your api key you can get the list id by running 'mc list lists'.
77
+
78
+ ## Todo
79
+ * Finish supporting all api calls
80
+ * Command-line tab auto-completion
81
+ * Better descriptions and help
82
+ * Complete testing coverage
83
+
84
+ ## License
85
+ See license.txt file.
86
+
87
+ ## Thanks
88
+ * Amro for the easy to use [gibbon](https://github.com/amro/gibbon) gem
89
+ * David Copeland's [gli](https://github.com/davetron5000/gli) command-line gem
90
+ * And of course MailChimp and their sweet API
@@ -0,0 +1,45 @@
1
+ require 'rake/clean'
2
+ require 'rubygems'
3
+ require 'rubygems/package_task'
4
+ require 'rdoc/task'
5
+ require 'cucumber'
6
+ require 'cucumber/rake/task'
7
+ Rake::RDocTask.new do |rd|
8
+ rd.main = "README.rdoc"
9
+ rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
10
+ rd.title = 'Your application title'
11
+ end
12
+
13
+ spec = eval(File.read('mc.gemspec'))
14
+
15
+ Gem::PackageTask.new(spec) do |pkg|
16
+ end
17
+ CUKE_RESULTS = 'results.html'
18
+ CLEAN << CUKE_RESULTS
19
+ desc 'Run features'
20
+ Cucumber::Rake::Task.new(:features) do |t|
21
+ opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
22
+ opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
23
+ t.cucumber_opts = opts
24
+ t.fork = false
25
+ end
26
+
27
+ desc 'Run features tagged as work-in-progress (@wip)'
28
+ Cucumber::Rake::Task.new('features:wip') do |t|
29
+ tag_opts = ' --tags ~@pending'
30
+ tag_opts = ' --tags @wip'
31
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
32
+ t.fork = false
33
+ end
34
+
35
+ task :cucumber => :features
36
+ task 'cucumber:wip' => 'features:wip'
37
+ task :wip => 'features:wip'
38
+ require 'rake/testtask'
39
+ Rake::TestTask.new do |t|
40
+ t.libs.push "lib"
41
+ t.test_files = FileList['test/*_test.rb']
42
+ t.verbose = true
43
+ end
44
+
45
+ task :default => [:test,:features]
data/bin/mc ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'mc'
3
+
4
+ run(ARGV)
@@ -0,0 +1,28 @@
1
+ Feature: The mc app has a useful user interface
2
+ As a user of the mc application
3
+ It should have a nice UI, since it's GLI-powered
4
+
5
+ Scenario: App just runs
6
+ When I get help for "mc"
7
+ Then the exit status should be 0
8
+
9
+ Scenario: Shows default global options
10
+ When I run `mc`
11
+ Then the output should contain "--apikey=apikey"
12
+
13
+ Scenario: Shows that api key is required if not supplied
14
+ When I run `mc helper ping` without a key or config file
15
+ Then the output should contain "Must include MailChimp API key either via --apikey flag or config file."
16
+
17
+ Scenario: Shows subcommand help
18
+ When I run `mc helper`
19
+ Then the output should contain "Command 'helper' requires a subcommand"
20
+
21
+ Scenario: Shows a successful ping
22
+ When I run `mc helper ping`
23
+ Then the output should contain "Everything's Chimpy!"
24
+
25
+ Scenario: Shows apikey
26
+ When I run `mc helper apikey`
27
+ Then the output should match /\w{32}-us\d.*/
28
+ Then the exit status should be 0
@@ -0,0 +1,14 @@
1
+ When /^I get help for "([^"]*)"$/ do |app_name|
2
+ @app_name = app_name
3
+ step %(I run `#{app_name} help`)
4
+ end
5
+
6
+ When /^I run `(.*)` without a key or config file$/ do |app_name|
7
+ @app_name = app_name
8
+ config_file_name = ENV['HOME']+ '/.mailchimp'
9
+ temp_file_name = config_file_name + '-temp'
10
+
11
+ File.rename(config_file_name, temp_file_name)
12
+ step %(I run `#{app_name}`)
13
+ File.rename(temp_file_name, config_file_name)
14
+ end
@@ -0,0 +1,15 @@
1
+ require 'aruba/cucumber'
2
+
3
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
4
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
5
+
6
+ Before do
7
+ # Using "announce" causes massive warnings on 1.9.2
8
+ @puts = true
9
+ @original_rubylib = ENV['RUBYLIB']
10
+ ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
11
+ end
12
+
13
+ After do
14
+ ENV['RUBYLIB'] = @original_rubylib
15
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'gli'
3
+ require 'colorize'
4
+
5
+ require 'mc/version'
6
+ require 'mc/helper'
7
+ require 'mc/commands'
8
+ require 'mc/mailchimp'
9
+ require 'mc/mailchimp_cached'
10
+ require 'mc/report'
11
+ require 'mc/writer'
@@ -0,0 +1,62 @@
1
+ include GLI::App
2
+ include Helper
3
+
4
+ program_desc 'Command line interface to MailChimp. Eep eep!'
5
+
6
+ version MC::VERSION
7
+
8
+ config_file '.mailchimp'
9
+
10
+ desc 'Turn on debugging'
11
+ switch [:debug], :negatable => false
12
+
13
+ desc 'Do not use cached result'
14
+ switch [:skipcache], :negatable => false
15
+
16
+ desc 'MailChimp API Key'
17
+ arg_name 'apikey'
18
+ flag [:apikey]
19
+
20
+ desc 'List to use'
21
+ arg_name 'listID'
22
+ flag [:list]
23
+
24
+ desc 'formatted, raw, or awesome'
25
+ arg_name 'format'
26
+ flag [:output,:o]
27
+
28
+ commands_from "mc/commands"
29
+
30
+ pre do |global,command,options,args|
31
+ # quit if no api
32
+ raise "Must include MailChimp API key either via --apikey flag or config file." if global[:apikey].nil?
33
+
34
+ # setup mailchimp api
35
+ @mailchimp = MailChimp.new(global[:apikey], {:debug => global[:debug]})
36
+ @mailchimp_cached = MailChimpCached.new(global[:apikey], {:debug => global[:debug], :skip_cache => global[:skipcache]})
37
+
38
+ # create cli writer
39
+ @output = ConsoleWriter.new(global)
40
+
41
+ # setup debug
42
+ @debug = true if global[:debug]
43
+
44
+ true
45
+ end
46
+
47
+ post do |global,command,options,args|
48
+ # put a space after each call
49
+ puts "\n"
50
+ end
51
+
52
+ on_error do |exception|
53
+ puts exception.message
54
+
55
+ if @debug then
56
+ puts "\n"+"-"*20+"[ Backtrace: ]"+"-"*20
57
+ exception.backtrace.each do |b|
58
+ puts b
59
+ end
60
+ end
61
+ false
62
+ end
@@ -0,0 +1,207 @@
1
+ desc 'Campaign related tasks'
2
+ command :campaigns do |c|
3
+
4
+ c.desc 'Create Campaign'
5
+ c.command :create do |s|
6
+ #TODO: support absplit, rss, auto campaign types
7
+ s.desc 'Type of campaign to create: regular, plaintext'
8
+ s.flag :type, :default_value => 'regular'
9
+
10
+ s.desc 'List ID'
11
+ s.flag [:id]
12
+
13
+ s.desc 'Subject'
14
+ s.flag [:subject]
15
+
16
+ s.desc 'From email'
17
+ s.flag ['from-email']
18
+
19
+ s.desc 'From name'
20
+ s.flag ['from-name']
21
+
22
+ s.desc 'To name'
23
+ s.flag ['to-name']
24
+
25
+ s.desc 'Template ID'
26
+ s.flag 'template-id'
27
+
28
+ s.desc 'Gallery template ID'
29
+ s.flag ['gallery-template-id']
30
+
31
+ s.desc 'Base template ID'
32
+ s.flag ['base-template-id']
33
+
34
+ s.desc 'Folder ID'
35
+ s.flag ['folder-id']
36
+
37
+ #s.desc 'Tracking'
38
+ #s.flag [:tracking]
39
+
40
+ s.desc 'Title - internal name to use'
41
+ s.flag [:title]
42
+
43
+ s.desc 'authenticate'
44
+ s.switch :authenticate, :negatable => true
45
+
46
+ #s.desc 'Analytics'
47
+ #s.flag [:analytics]
48
+
49
+ s.desc 'Auto footer?'
50
+ s.switch 'auto-footer'
51
+
52
+ s.desc 'Enable inline css?'
53
+ s.switch 'inline-css', :negatable => false
54
+
55
+ s.desc 'Auto tweet?'
56
+ s.switch :tweet, :negatable => false
57
+
58
+ #s.desc 'Auto FB post'
59
+ #s.flag 'auto-fb-post'
60
+
61
+ s.desc 'FB comments?'
62
+ s.switch 'fb-comments', :negatable => false
63
+
64
+ s.desc 'Enable timewarp'
65
+ s.switch :timewarp, :negatable => false
66
+
67
+ s.desc 'HTML filename to load'
68
+ s.flag 'html-filename'
69
+
70
+ s.desc 'TXT filename to load'
71
+ s.flag 'text-filename'
72
+
73
+ s.desc 'segmentation options [NAME,VALUE]'
74
+ s.flag 'seg-options'
75
+
76
+ s.action do |global,options,args|
77
+ if options[:seg_options]
78
+ field, value = options[:seg_options].split(",") if options[:seg_options]
79
+ segment_conditions = [{:field => field, :op => "like", :value => value}]
80
+ end
81
+
82
+ type = options[:type]
83
+
84
+ standard_opts = {
85
+ :list_id => options[:id],
86
+ :subject => required_option(:subject, options[:subject]),
87
+ :from_email => required_option('from-email', options['from-email']),
88
+ :from_name => required_option('from-name', options['from-name']),
89
+ :to_name => options['to-name'],
90
+ :template_id => options['template-id'],
91
+ :gallery_template_id => options['gallery-template-id'],
92
+ :base_template_id => options['base-template-id'],
93
+ :folder => options['folder-id'],
94
+ :inline_css => true,
95
+ :tracking => {:opens => true, :html_clicks => true, :text_clicks => false},
96
+ :title => options['title'],
97
+ :authenticate => options[:authenticate],
98
+ #:analytics
99
+ :auto_footer => options['auto-footer'],
100
+ :inline_css => options['inline-css'],
101
+ :generate_text => options['generate-text'],
102
+ :auto_tweet => options['auto-tweet'],
103
+ :auto_fb_post => options['auto-fb-posts'],
104
+ :fb_comments => options['fb-comments'],
105
+ :timewarp => options[:timewarp],
106
+ :ecomm360 => options[:ecomm360]
107
+ #:crm_tracking
108
+ }
109
+
110
+ html = File.open(options['html-filename']).read if options['html-filename']
111
+ text = File.open(options['text-filename']).read if options['text-filename']
112
+ content = {
113
+ :html => html,
114
+ :text => text
115
+ }
116
+
117
+ segment_opts = {:match => "all", :conditions => segment_conditions} unless segment_conditions.nil?
118
+ type_opts = {}
119
+
120
+ campaign = @mailchimp.campaigns_create(:type => type, :options => standard_opts, :content => content, :segment_opts => segment_opts, :type_opts => type_opts)
121
+ puts "Created new campaign with id = #{campaign['id']}"
122
+ end
123
+ end
124
+
125
+ c.desc 'Send Campaign Now'
126
+ c.command :send do |s|
127
+ s.action do |global,options,args|
128
+ campaign_id = required_argument("Need to supply a campaign id", args.first)
129
+ puts @mailchimp.campaigns_send :cid => campaign_id
130
+ end
131
+ end
132
+
133
+ c.desc 'Schedule Campaign'
134
+ c.command :schedule do |s|
135
+ s.desc 'Date to schedule campaign in YYYY-MM-DD format'
136
+ s.flag :date, :default_value => (Time.now + 86400).strftime("%Y-%m-%d")
137
+
138
+ s.desc 'Time to schedule campaign at in HH:MM:SS format'
139
+ s.flag :time, :default_value => '08:00:00'
140
+
141
+ s.action do |global,options,args|
142
+ campaign_id = options[:cid]
143
+ puts @mailchimp.campaigns_schedule(:cid => campaign_id, :schedule_time => options[:date] + ' ' + options[:time])
144
+ end
145
+ end
146
+
147
+ c.desc 'Send Test Campaign'
148
+ c.command :sendtest do |s|
149
+ s.action do |global,options,args|
150
+ campaign_id = required_argument("Need to supply a campaign id", args.first)
151
+
152
+ puts "Sending test for campaign #{campaign_id}..."
153
+ puts @mailchimp.campaign_send_test(:cid=> campaign_id, :test_emails => ["kale.davis@gmail.com", "kale@simplerise.com"])
154
+ end
155
+ end
156
+
157
+ c.desc 'Delete Campaign'
158
+ c.command :delete do |s|
159
+ s.action do |global,options,args|
160
+ campaign_id = options[:cid] || @mailchimp.cache.get(:campaign_id)
161
+ throw "Need a valid Campaign ID." if campaign_id.nil?
162
+
163
+ campaign_delete(:cid=> campaign_id)
164
+ puts "Deleted campaign #{campaign_id}."
165
+ end
166
+ end
167
+
168
+ c.desc 'Get the list of campaigns and their details matching the specified filters'
169
+ c.command :list do |s|
170
+ s.switch :first
171
+ s.flag :start, :default_value => 0
172
+ s.flag :limit, :default_value => 50
173
+ s.flag :sort_field, :default_value => 'create_time'
174
+ s.flag :sort_dir, :default_value => 'DESC'
175
+ s.action do |global,options,args|
176
+ @output.standard @mailchimp.campaigns_list(:limit => options[:limit])['data'], :fields => [:id, :title, :status, :send_time, :emails_sent, :archive_url]
177
+ end
178
+ end
179
+
180
+ c.desc 'Check to see if campaign is ready to send'
181
+ c.command :ready do |s|
182
+ s.action do |global,options,args|
183
+ cid = options[:cid] || get_last_campaign_id
184
+
185
+ puts cid
186
+ @output.standard @mailchimp_cached.campaigns_ready(:cid=> cid)
187
+ end
188
+ end
189
+
190
+ c.command "segment-test" do |s|
191
+ s.desc "Use either 'any' or 'all'"
192
+ s.flag :match, :default_value => 'all'
193
+
194
+ s.desc 'Condition in the format field,op,value'
195
+ s.flag :condition
196
+ s.action do |global,options,args|
197
+ id = get_required_argument(:id, options[:id], global[:default_list])
198
+
199
+ segment = {}
200
+ segment['match'] = options[:match]
201
+ field, op, value = options[:condition].split(',')
202
+ segment['conditions'] = [{:field => field, :op => op, :value => value}]
203
+
204
+ puts @mailchimp.campaigns_segment_test(:list_id => id, :options => segment)
205
+ end
206
+ end
207
+ end