cloudapp-power-cli 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/Gemfile +3 -0
  2. data/README.md +77 -0
  3. data/bin/cloudapp +506 -0
  4. data/cloudapp-power-cli.gemspec +17 -0
  5. metadata +62 -0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # cloudapp-power-cli
2
+
3
+ A powerful CLI for [CloudApp](http://getcloudapp.com/) written in Ruby, formerly named *cloudapp-cli* that makes the complete CloudApp API available on command line.
4
+
5
+ **[Works with regenwolken](https://github.com/posativ/regenwolken)**
6
+
7
+ ## Installation
8
+
9
+ Install it via:
10
+
11
+ gem install cloudapp-power-cli
12
+
13
+ ## Usage
14
+
15
+ Register:
16
+
17
+ cloudapp register us3r passw0rd
18
+
19
+ Configuration of persistent login data:
20
+
21
+ cloudapp login us3r passw0rd
22
+
23
+ (Note: If you tell it so e.g. via the above command or during `cloudapp register`, the cli will save your login data to '~/.cloudapp-cli' to prevent you from specifing it on every call.)
24
+
25
+ Use per-call login data (**no** login data will be written to disk at all):
26
+
27
+ cloudapp -u user:pwd command...
28
+
29
+ Other Operations:
30
+
31
+ cloudapp account # show your account details (-v shows even more)
32
+
33
+ cloudapp list # list first page of drops (5 per page)
34
+
35
+ cloudapp list -p 2 -n 10 # list second page of drops with 10 drops per page
36
+
37
+ cloudapp list -a # list all your drops (first 100 ^^)
38
+
39
+ cloudapp upload drumandbass.ogg # upload a file
40
+
41
+ cloudapp upload --private topsecret.pdf # upload a file as private
42
+
43
+ cloudapp bookmark heise http://heise.de/ # creates an alias
44
+
45
+ cloudapp bookmark http://google.com/ # the url will be the alias' name
46
+
47
+ cloudapp download dZ69 # download a drop to current directory
48
+
49
+ cloudapp view dZ69 # view details of a drop (more details with -v)
50
+
51
+ cloudapp change username thenewname # changes e-mail and updates the local credentials, too
52
+
53
+ cloudapp change password thenewpwd # changes password and updates the local credentials, too
54
+
55
+ cloudapp change privacy private # change default security of _new_ drops to private (public is the other option)
56
+
57
+ cloudapp private dZ69 # change a drop to private
58
+
59
+ cloudapp rename dZ69 UserGuide.txt # rename a drop
60
+
61
+ cloudapp delete dZ69 # delete a drop
62
+
63
+ cloudapp recover dZ69 # recover a drop
64
+
65
+ cloudapp search Screenshot # list all items with "Screenshot" in name or redirect url (for bookmarks)
66
+
67
+ cloudapp search "^.*rb$" # since the term is effectively a regex you could do many magic for filtering
68
+
69
+ cloudapp gc-view GIFT1234 # view gift card details
70
+
71
+ cloudapp gc-redeem GIFT1234 # redeem a gift card
72
+
73
+ Note: If you want to specify arguments (e.g. item IDs or file names) with a leading dash, the Option Parser will claim about an unknown option - than you should insert an double-dash followed by a space in front of the argument with the leading dash.
74
+
75
+ ## Credits
76
+
77
+ To [posativ](https://github.com/posativ) for testing and bug reporting!
data/bin/cloudapp ADDED
@@ -0,0 +1,506 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'date'
5
+ require 'optparse'
6
+ require 'uri'
7
+ require 'yaml'
8
+
9
+ require 'rubygems'
10
+ require 'cloudapp_api'
11
+
12
+ LIST_TIME_FMT = '%d.%m.%y %H:%M'
13
+ VIEW_TIME_FMT = '%d. %b %Y %H:%M:%S'
14
+ ACC_TIME_FMT = VIEW_TIME_FMT
15
+
16
+ ITEM_TYPES = [:image, :bookmark, :text, :archive, :audio, :video, :unknown]
17
+
18
+ # just a guess to make output format prettier
19
+ HASH_LENGTH = 10
20
+
21
+ # low level errors
22
+ LOW_ERRORS = [
23
+ #TimeoutError, EOFError, Errno::ETIMEDOUT, Errno::ECONNREFUSED,
24
+ Errno::EPIPE, Errno::EINVAL
25
+ ]
26
+
27
+ module CloudApp
28
+ class Drop
29
+ def slug
30
+ url.split(/\//).last
31
+ end
32
+
33
+ # patch for cloudapp_api supporting custom domains
34
+ def self.find(id)
35
+ res = get "http://#{$domain}/#{id}"
36
+ res.ok? ? Drop.new(res) : bad_response(res)
37
+ end
38
+ end
39
+ end
40
+
41
+ class Module
42
+ def subclasses
43
+ classes = []
44
+ ObjectSpace.each_object do |klass|
45
+ next unless Module === klass
46
+ classes << klass if self > klass
47
+ end
48
+ classes
49
+ end
50
+ end
51
+
52
+ class String
53
+ def json2time
54
+ # offset should be zero since this is assumed to be UTC
55
+ dt = DateTime.strptime(self, "%Y-%m-%dT%H:%M:%SZ")
56
+ Time.utc(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec)
57
+ # "2011-09-24T08:49:14Z"
58
+ end
59
+
60
+ def localtime
61
+ json2time.localtime
62
+ end
63
+
64
+ def json2fmtlocaltime(fmt)
65
+ json2time.localtime.strftime(fmt)
66
+ end
67
+ end
68
+
69
+ class Time
70
+ # HACK: this is a hack to ensure older httparty then 0.8.0 will work too
71
+ def json2fmtlocaltime(fmt)
72
+ localtime.strftime(fmt)
73
+ end
74
+ end
75
+
76
+ def args?(n=2)
77
+ if ARGV.length < n
78
+ puts @opts
79
+ abort "Wrong number of arguments given!"
80
+ end
81
+ end
82
+
83
+ # only use bold colors (1;) for those on black on white screens
84
+ def cyan; if @options[:colors]; "\e[1;36m" else "" end end
85
+ def green; if @options[:colors]; "\e[1;32m" else "" end end
86
+ def red; if @options[:colors]; "\e[1;31m" else "" end end
87
+ def rst; if @options[:colors]; "\e[0m" else "" end end
88
+
89
+ def print_long(drop)
90
+ list_flags = ''
91
+ if drop.private; list_flags += [green, 'p', rst].join else list_flags += '-' end
92
+ if !drop.deleted_at.nil?; list_flags += [red, 'd', rst].join else list_flags += '-' end
93
+ if not drop.redirect_url.nil?
94
+ list_name = ["-> ", cyan, drop.redirect_url, rst].join
95
+ # prepend the drop's name, if it's different from the url
96
+ # (which should only happen if you specify it by hand)
97
+ list_name = [cyan, drop.name, rst, " ", list_name].join if drop.name != drop.redirect_url
98
+ elsif drop.item_type == 'image'
99
+ list_name = [green, drop.name, rst].join
100
+ else
101
+ list_name = drop.name
102
+ end
103
+ format("%-#{HASH_LENGTH}s %s %-9s %-10s %-#{8 + $domain.length + HASH_LENGTH}s %s",
104
+ drop.slug, list_flags, drop.item_type,
105
+ drop.updated_at.json2fmtlocaltime(LIST_TIME_FMT),
106
+ drop.url, list_name)
107
+ end
108
+
109
+ def fetch(url, limit = 3)
110
+ raise ArgumentError, 'HTTP redirection too deep' if limit == 0
111
+
112
+ response = Net::HTTP.get_response(URI.parse(url))
113
+ case response
114
+ when Net::HTTPSuccess then response
115
+ when Net::HTTPRedirection then fetch(response['location'], limit-1)
116
+ else response.error! end
117
+ end
118
+
119
+ def load_config
120
+ if @options.key?(:creds)
121
+ u,p = @options[:creds].split(':')
122
+ return {'username' => u, 'password' => p}
123
+ end
124
+ if !File.exists?($config_file)
125
+ abort "Login required! Please save your login via ´login´ or use ´-u´ option."
126
+ end
127
+ YAML.load_file($config_file)
128
+ end
129
+
130
+ def save_config(config)
131
+ if File.exists?($config_file)
132
+ config = YAML.load_file($config_file).merge(config)
133
+ end
134
+ File.open($config_file, 'w+') do |f| YAML.dump(config, f) end
135
+ end
136
+
137
+ def load_service_url
138
+ return @options[:service_url] if @options.key?(:service_url)
139
+ if File.exists?($config_file)
140
+ config = YAML.load_file($config_file)
141
+ return config['service_url'] if config['service_url']
142
+ end
143
+ "http://my.cl.ly"
144
+ end
145
+
146
+ # main starts here
147
+
148
+ @options = {:colors => true}
149
+
150
+ @opts = OptionParser.new do |opts|
151
+ opts.banner = "Usage: cloudapp [options] command [arguments]\n\n"
152
+ opts.banner += "Commands:\n"
153
+ opts.banner += " register USERNAME PASSWORD\n"
154
+ opts.banner += " login [USERNAME] [PASSWORD]\n"
155
+ opts.banner += " service [URL]\n"
156
+ opts.banner += " change (username|password|privacy) NEWVALUE\n"
157
+ opts.banner += " account\n"
158
+ opts.banner += " list\n"
159
+ opts.banner += " upload FILE\n"
160
+ opts.banner += " bookmark [NAME] LINK\n"
161
+ opts.banner += " download SLUG\n"
162
+ opts.banner += " view SLUG\n"
163
+ opts.banner += " delete SLUG\n"
164
+ opts.banner += " recover SLUG\n"
165
+ opts.banner += " rename SLUG NAME\n"
166
+ opts.banner += " private SLUG\n"
167
+ opts.banner += " public SLUG\n"
168
+ opts.banner += " search NAME\n"
169
+ opts.banner += " gc-view CODE\n"
170
+ opts.banner += " gc-redeem CODE\n"
171
+ opts.banner += " auto-purge HOURS\n"
172
+ opts.banner += "\nOptions:\n"
173
+
174
+ opts.on('-u CREDS', 'Specify credentials as USER:PASS') do |u| @options[:creds] = u end
175
+ opts.on('-p PAGE', Integer, 'Show page with nr. PAGE in `list` (default: 1)') do |p| @options[:page] = p if p > 0 end
176
+ opts.on('-n ITEMSPP', Integer, 'Show ITEMSPP items per page in `list` (default: 5)') do |n| @options[:per_page] = n if n > 0 end
177
+ opts.on('-t TYPE', '--type TYPE', ITEM_TYPES, 'Show only items of TYPE in `list`') do |t| @options[:type] = t end
178
+ opts.on('-s SOURCE', '--source SOURCE', 'Show only items from SOURCE in ´list´') do |s| @options[:source] = s end
179
+ opts.on('-d', '--deleted', 'Show deleted items in ´list´ too') do |d| @options[:deleted] = true end
180
+ opts.on('-a', '--all', 'Show all items of an account at once in `list` (overrides -p, -n)') do |a| @options[:all] = true end
181
+ opts.on('--private', 'Do private upload') do |priv| @options[:private] = true end
182
+ opts.on('--public', 'Do public upload') do |pub| @options[:public] = true end
183
+ opts.on('--disable-colors', 'Disables colors') do |dis| @options[:colors] = false end
184
+ opts.on('--service URL', 'Specify service URL') do |url| @options[:service_url] = url end
185
+ opts.on('-y', '--yes', 'Answer all questions with yes (useful e.g. in scripts)') do |y| @options[:yes] = true end
186
+ opts.on('-v', '--verbose', 'Enables verbose mode on some commands (currently view, account)') do |v| @options[:verbose] = true end
187
+ opts.on('-h', '-?', '--help', 'Display this screen') do
188
+ puts opts
189
+ exit
190
+ end
191
+ end
192
+
193
+ @opts.parse!
194
+
195
+ $config_file = File.join(ENV['HOME'], '.cloudapp-cli')
196
+
197
+ # HACK: inject our service url as HTTParty base_uri into
198
+ # all subclasses of CloudApp::Base
199
+ service_url = load_service_url
200
+ CloudApp::Base.subclasses.each do |c|
201
+ c.base_uri service_url
202
+ end
203
+
204
+ case ARGV.first
205
+ when nil
206
+ puts @opts
207
+ exit
208
+ when 'login'
209
+ if ARGV[1] and ARGV[2]
210
+ client = CloudApp::Client.new
211
+ client.authenticate(ARGV[1], ARGV[2])
212
+ save = true
213
+ begin
214
+ CloudApp::Account.find
215
+ rescue => ex
216
+ save = false
217
+ end
218
+ if !save
219
+ if @options[:yes]
220
+ puts "Saving login though it seems incorrect."
221
+ save = true
222
+ elsif $stdin.tty?
223
+ print "Your new login seems to be incorrect, save anyways? (y/n): "
224
+ answer = $stdin.gets
225
+ case answer
226
+ when "y\n"
227
+ save = true
228
+ else
229
+ puts "You didn't answer with 'y', done nothing."
230
+ end
231
+ end
232
+ end
233
+ if save
234
+ # set
235
+ save_config({'username' => ARGV[1], 'password' => ARGV[2]})
236
+ puts 'Account login saved.'
237
+ end
238
+ else
239
+ # get
240
+ config = load_config
241
+ puts "Current login:"
242
+ puts " Username: #{config['username']}"
243
+ puts " Password: #{config['password']}"
244
+ end
245
+ exit 0
246
+ when 'register'
247
+ args? 3
248
+ begin
249
+ acc = CloudApp::Account.create :email => ARGV[1], :password => ARGV[2], :accept_tos => true
250
+ rescue CloudApp::ResponseError => ex
251
+ abort "Username already registered!" if ex.code == 406
252
+ abort "Server rejected request because of invalid username/email or password!" if ex.code = 422
253
+ abort "Registration failed (#{ex.to_s}) - may be a bug"
254
+ rescue => ex
255
+ abort "Registration failed (#{ex.to_s})"
256
+ end
257
+ puts "Successfully registered!\nYour account has been activated instantly, happy clouding!" if not acc.activated_at.nil?
258
+ puts "Successfully registered but your account isn't currently activated." if acc.activated_at.nil?
259
+ if @options[:yes]
260
+ puts "Saving login to local login storage #{$config_file}."
261
+ save_config({'username' => ARGV[1], 'password' => ARGV[2]})
262
+ elsif $stdin.tty?
263
+ if File.exists?($config_file)
264
+ print "Do you want to overwrite your local login storage #{$config_file}? (y/n): "
265
+ else
266
+ print "Do you want to save your login to #{$config_file} for automated use? (y/n): "
267
+ end
268
+ answer = $stdin.gets
269
+ case answer
270
+ when "y\n"
271
+ save_config({'username' => ARGV[1], 'password' => ARGV[2]})
272
+ puts "Account registered and login saved."
273
+ else
274
+ puts "You didn't answer with 'y', done nothing."
275
+ end
276
+ end
277
+ exit 0
278
+ when 'service'
279
+ if ARGV[1]
280
+ uri = ARGV[1]
281
+ uri = 'http://' + uri if URI.parse(ARGV[1]).scheme.nil?
282
+ if (uri =~ URI::regexp).nil?
283
+ puts "Your new service is not a valid URL, done nothing."
284
+ else
285
+ # set
286
+ save_config({'service_url' => uri})
287
+ puts "New service saved."
288
+ end
289
+ else
290
+ # get
291
+ puts "Current service: #{service_url}"
292
+ end
293
+ exit 0
294
+ end
295
+
296
+ config = load_config
297
+
298
+ @client = CloudApp::Client.new
299
+
300
+ # all following commands need authentication
301
+ @client.authenticate(config['username'], config['password'])
302
+ begin
303
+ @acc = CloudApp::Account.find
304
+ $domain = @acc.domain.nil? ? 'cl.ly' : @acc.domain
305
+ rescue CloudApp::ResponseError => ex
306
+ abort "Incorrect login!" if ex.code == 403
307
+ abort "Your account hasn't been activated!" if ex.code == 409
308
+ abort "Authentication unexpectedly failed: #{ex.to_s}\n #{ex.backtrace.join("\n ")}"
309
+ end
310
+
311
+ begin
312
+ case ARGV.first
313
+ when 'list'
314
+ topt = {:page => 1, :per_page => 5}
315
+
316
+ topt[:page] = @options[:page] if @options.key?(:page)
317
+ topt[:per_page] = @options[:per_page] if @options.key?(:per_page)
318
+ # HACK: 100 items should be enough for everyone!
319
+ topt[:page], topt[:per_page] = 1, 100 if @options[:all]
320
+ topt[:type] = @options[:type] if @options.key?(:type)
321
+ topt[:deleted] = @options[:deleted] if @options.key?(:deleted)
322
+ topt[:source] = @options[:source] if @options.key?(:source)
323
+
324
+ # caption
325
+ puts format("%-#{HASH_LENGTH}s %s", "SLUG", "p=private, d=deleted")
326
+
327
+ @client.drops(topt).each do |drop|
328
+ puts print_long(drop)
329
+ end
330
+
331
+ when 'view'
332
+ args?
333
+ drop = @client.drop ARGV[1]
334
+ puts "Details for #{drop.slug}:"
335
+ puts " Name: #{drop.name}"
336
+ puts " Type: #{drop.item_type}"
337
+ puts " URL: #{drop.url}"
338
+ puts " Privacy: #{drop.private ? 'private' : 'public'}"
339
+ puts " Views: #{drop.view_counter}"
340
+ puts " Redirect: #{drop.redirect_url}" if !drop.redirect_url.nil?
341
+ puts " Created: #{drop.created_at.json2fmtlocaltime(VIEW_TIME_FMT)}"
342
+ puts " Updated: #{drop.updated_at.json2fmtlocaltime(VIEW_TIME_FMT)}"
343
+ puts " Deleted: #{drop.deleted_at.json2fmtlocaltime(VIEW_TIME_FMT)}" if !drop.deleted_at.nil?
344
+ puts " Href: #{drop.href}" if @options[:verbose]
345
+ puts " Source: #{drop.source}" if @options[:verbose]
346
+
347
+ when 'change'
348
+ args? 3
349
+ case ARGV[1]
350
+ when 'username'
351
+ CloudApp::Account.update :email => ARGV[2], :current_password => config['password']
352
+ # only update local credential storage when current creds *not* given via command line
353
+ if @options[:creds].nil?
354
+ save_config({'username' => ARGV[2]})
355
+ puts "Login changed, updated #{$config_file}."
356
+ end
357
+ when 'password'
358
+ CloudApp::Account.update :password => ARGV[2], :current_password => config['password']
359
+ # only update local credential storage when current creds *not* given via command line
360
+ if @options[:creds].nil?
361
+ save_config({'password' => ARGV[2]})
362
+ puts "Login changed, updated #{$config_file}."
363
+ end
364
+ when 'privacy'
365
+ CloudApp::Account.update :private_items => (ARGV[2] == 'private')
366
+ end
367
+
368
+ when 'account'
369
+ # use @acc and stats
370
+ stats = CloudApp::Account.stats
371
+ puts "Account '#{config[:username]}':"
372
+ puts " ID: #{@acc.id}"
373
+ puts " Username: #{@acc.email}"
374
+ puts " Default privacy: #{@acc.private_items ? 'private' : 'public'}"
375
+ puts " Subscription: #{@acc.subscribed ? 'yes' : 'no'}" +
376
+ (@acc.subscribed ? " (expires: #{@acc.subscription_expires_at})" : '')
377
+ puts " Domain: #{@acc.domain}"
378
+ puts " Domain homepage: #{@acc.domain_home_page}"
379
+ puts " Created: #{@acc.created_at.json2fmtlocaltime(ACC_TIME_FMT)}"
380
+ puts " Updated: #{@acc.updated_at.json2fmtlocaltime(ACC_TIME_FMT)}" if @options[:verbose]
381
+ puts " Activated: #{@acc.activated_at.json2fmtlocaltime(ACC_TIME_FMT)}" if @options[:verbose]
382
+ puts
383
+ puts "Stats:"
384
+ puts " Nr. of items: #{stats[:items]}"
385
+ puts " Nr. of total views: #{stats[:views]}"
386
+
387
+ when 'upload'
388
+ args?
389
+ # TODO: Notify the user when exceeding upload limits
390
+ begin
391
+ if @options[:private]
392
+ drop = @client.upload ARGV[1], :private => true
393
+ elsif @options[:public]
394
+ drop = @client.upload ARGV[1], :private => false
395
+ else
396
+ drop = @client.upload ARGV[1]
397
+ end
398
+ puts "#{drop.url}"
399
+ rescue *LOW_ERRORS => ex
400
+ abort "Upload failed maybe due to too large file or server limits!"
401
+ end
402
+
403
+ when 'bookmark'
404
+ if ARGV.length == 2
405
+ drop = @client.bookmark ARGV[1], ARGV[1]
406
+ puts "Created bookmark #{drop.slug}, URL:\n#{drop.url}"
407
+ else
408
+ args? 3
409
+ drop = @client.bookmark ARGV[1], ARGV[2]
410
+ puts "Created bookmark #{drop.slug}, URL:\n#{drop.url}"
411
+ end
412
+
413
+ when 'private'
414
+ args?
415
+ drop = @client.privacy ARGV[1], true
416
+ puts "#{drop.slug} is now private"
417
+
418
+ when 'public'
419
+ args?
420
+ drop = @client.privacy ARGV[1], false
421
+ puts "#{drop.slug} is now public"
422
+
423
+ when 'delete','remove'
424
+ args?
425
+ drop = @client.delete ARGV[1]
426
+ puts "Moved #{drop.slug} into trash"
427
+
428
+ when 'recover'
429
+ args?
430
+ drop = @client.recover ARGV[1]
431
+ puts "Recovered #{drop.slug} from trash"
432
+
433
+ when 'rename'
434
+ args? 3
435
+ drop = @client.rename ARGV[1], ARGV[2]
436
+ puts "#{ARGV[1]} renamed to #{ARGV[2]}"
437
+
438
+ when 'download'
439
+ args?
440
+ drop = @client.drop ARGV[1]
441
+ if drop.item_type == 'bookmark'
442
+ abort "Cannot download bookmarks!"
443
+ end
444
+ # File name filtering - this build up from a union of NTFS, FAT, HFS, ext3
445
+ file_name = drop.name.gsub(/[\x00\/\\:\*\?\"<>\|\^]/, '_')
446
+ if File.exists?(file_name)
447
+ abort "Target file '#{file_name}' already exists! Canceling..."
448
+ end
449
+ res = fetch(drop.content_url)
450
+ File.open(file_name, 'w') do |file|
451
+ file.write(res.body)
452
+ end
453
+ puts "Downloaded #{drop.slug} as '#{file_name}'."
454
+
455
+ when 'search'
456
+ args?
457
+ @client.drops({:page => 1, :per_page => 100}).each do |drop|
458
+ # check drop.name and drop.redirect_url
459
+ if drop.name.match(ARGV[1]) or (!drop.redirect_url.nil? and drop.redirect_url.match(ARGV[1]))
460
+ puts print_long(drop)
461
+ end
462
+ end
463
+
464
+ when 'gc-view'
465
+ args?
466
+ gift = CloudApp::GiftCard.find ARGV[1]
467
+ puts "Details for gift card #{gift.code}:"
468
+ puts " ID: #{gift.id}"
469
+ puts " Plan: #{gift.plan}"
470
+ puts " Months: #{gift.months}"
471
+ puts " Href: #{gift.href}"
472
+ puts " Created: #{gift.created_at.json2fmtlocaltime(VIEW_TIME_FMT)}"
473
+ puts " Updated: #{gift.updated_at.json2fmtlocaltime(VIEW_TIME_FMT)}" if @options[:verbose]
474
+ puts " Redeemed: #{gift.redeemed_at.json2fmtlocaltime(VIEW_TIME_FMT)}" if !gift.redeemed_at.nil?
475
+ puts " Effective: #{gift.effective_at.json2fmtlocaltime(VIEW_TIME_FMT)}" if !gift.effective_at.nil?
476
+ puts " Expires: #{gift.expires_at.json2fmtlocaltime(VIEW_TIME_FMT)}" if !gift.expires_at.nil?
477
+
478
+ when 'gc-redeem'
479
+ args?
480
+ gift = CloudApp::GiftCard.redeem ARGV[1]
481
+ puts "Redeemed gift card #{gift.code}"
482
+
483
+ when 'auto-purge'
484
+ args?
485
+ hours = ARGV[1].to_i
486
+ abort "Hours given are negative" if hours < 0
487
+ puts "Request to delete all items created more than #{hours} hours ago from now"
488
+ latest = Time.now - hours*60*60
489
+ @client.drops({:page => 1, :per_page => 1000}).each do |drop|
490
+ if drop.created_at.localtime < latest
491
+ puts "Deleting #{drop.slug}"
492
+ @client.delete drop.slug
493
+ end
494
+ end
495
+ puts "Done"
496
+
497
+ else
498
+ puts @opts
499
+ abort "Unknown command!"
500
+ end
501
+ rescue CloudApp::ResponseError => ex
502
+ abort "Item not found!" if ex.code == 404
503
+ abort "File too large! (#{ex.to_s})" if ex.code == 413
504
+ abort "Your account hasn't been activated!" if ex.code == 409
505
+ abort "#{ARGV.first} unexpectedly failed: #{ex.to_s}\n #{ex.backtrace.join("\n ")}"
506
+ end
@@ -0,0 +1,17 @@
1
+
2
+ Gem::Specification.new do |s|
3
+ s.name = "cloudapp-power-cli"
4
+ s.version = "1.0.0"
5
+ s.summary = "A powerful CLI for CloudApp written in Ruby."
6
+ s.description = "A powerful CLI for CloudApp that makes the complete CloudApp API available on command line and works with Regenwolken."
7
+ s.author = "Christian Nicolai"
8
+ s.email = "cn@mycrobase.de"
9
+ s.license = "Apache License Version 2.0"
10
+ s.homepage = "https://github.com/cmur2/cloudapp-power-cli"
11
+
12
+ s.files = `git ls-files`.split($/)
13
+ s.executables = ["cloudapp"]
14
+ s.require_paths = ["lib"]
15
+
16
+ s.add_runtime_dependency "cloudapp_api"
17
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudapp-power-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christian Nicolai
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-29 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cloudapp_api
16
+ requirement: &79873160 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *79873160
25
+ description: A powerful CLI for CloudApp that makes the complete CloudApp API available
26
+ on command line and works with Regenwolken.
27
+ email: cn@mycrobase.de
28
+ executables:
29
+ - cloudapp
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - Gemfile
34
+ - README.md
35
+ - bin/cloudapp
36
+ - cloudapp-power-cli.gemspec
37
+ homepage: https://github.com/cmur2/cloudapp-power-cli
38
+ licenses:
39
+ - Apache License Version 2.0
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 1.8.6
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: A powerful CLI for CloudApp written in Ruby.
62
+ test_files: []