twitter_backup 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +30 -0
- data/Rakefile +2 -0
- data/bin/twitter_backup +49 -0
- data/lib/twitter_backup.rb +47 -0
- data/lib/twitter_backup/config.rb +138 -0
- data/lib/twitter_backup/error.rb +7 -0
- data/lib/twitter_backup/tweet.rb +116 -0
- data/lib/twitter_backup/ui.rb +89 -0
- data/lib/twitter_backup/version.rb +3 -0
- data/twitter_backup.gemspec +25 -0
- metadata +156 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 il.zoff
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# TwitterBackup
|
2
|
+
|
3
|
+
This gem will download your tweets from Twitter and save them to sqlite3 database and plaintext (yaml) archive file.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
$ gem install twitter_backup
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
First of all, you need to create an app at https://dev.twitter.com/apps
|
12
|
+
Your app will be assigned a 'consumer key'/'consumer secret' pair and you as a user will be assigned an 'access token'/'acces token secret' OAuth pair for that application.
|
13
|
+
Without these credentials you won't be able to use TwitterBackup.
|
14
|
+
|
15
|
+
Now, all you have to do is to run
|
16
|
+
|
17
|
+
$ twitter_backup
|
18
|
+
|
19
|
+
If you want to know, what's happening during your backup process, use verbose mode with `-v`.
|
20
|
+
|
21
|
+
$ twitter_backup -h
|
22
|
+
-v, --verbose Enable verbose mode
|
23
|
+
-f, --force Try to download tweets, even if it seems useless
|
24
|
+
-s, --seed Try to download tweets older than the oldest one you have
|
25
|
+
-c, --config Config file. Default: ~/.config/twitter_backup/config.yml
|
26
|
+
-h, --help Display this help message.
|
27
|
+
|
28
|
+
## TODO
|
29
|
+
|
30
|
+
- Automate adding this script to cron?
|
data/Rakefile
ADDED
data/bin/twitter_backup
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
# require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib twitter_backup]))
|
5
|
+
require "twitter_backup"
|
6
|
+
require "twitter_backup/ui"
|
7
|
+
|
8
|
+
include TwitterBackup
|
9
|
+
|
10
|
+
if TBConfig.passed_opts.help?
|
11
|
+
TBConfig.passed_opts.help
|
12
|
+
exit 0
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
|
17
|
+
say ".... checking existance of credentials" if TBConfig.passed_opts.verbose?
|
18
|
+
UI.ask_credentials if TBConfig.credentials_missing?
|
19
|
+
|
20
|
+
UI.define_path_to_backup unless TBConfig.path_to_backup_defined?
|
21
|
+
|
22
|
+
TBConfig.configure
|
23
|
+
|
24
|
+
UI.greet_user
|
25
|
+
|
26
|
+
Tweet.update_tweets
|
27
|
+
Tweet.update_tweets(:from => :earliest) if TBConfig.passed_opts.seed?
|
28
|
+
|
29
|
+
Tweet.dump_to_backup_file
|
30
|
+
|
31
|
+
rescue Error::MissingCredentials => error
|
32
|
+
UI.missing_credentials_exit
|
33
|
+
|
34
|
+
rescue Twitter::Error::Unauthorized => error
|
35
|
+
TBConfig.empty_credentials
|
36
|
+
UI.wrong_credentials_exit
|
37
|
+
|
38
|
+
rescue Twitter::Error::TooManyRequests => error
|
39
|
+
UI.too_many_requests_exit
|
40
|
+
|
41
|
+
rescue Error::InvalidBackupFile => error
|
42
|
+
UI.failed_backup_path_exit( error.message )
|
43
|
+
|
44
|
+
rescue Error::InvalidPath => error
|
45
|
+
say %[<%= color("#{error.message}", :red) %>]
|
46
|
+
exit 1
|
47
|
+
ensure
|
48
|
+
UI.exit_screen
|
49
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "slop"
|
2
|
+
require "yaml"
|
3
|
+
require "fileutils"
|
4
|
+
require "highline/import"
|
5
|
+
require 'active_support/core_ext/string'
|
6
|
+
require "active_record"
|
7
|
+
require "twitter"
|
8
|
+
require_relative "twitter_backup/version"
|
9
|
+
require_relative "twitter_backup/error"
|
10
|
+
require_relative "twitter_backup/config"
|
11
|
+
require_relative "twitter_backup/tweet"
|
12
|
+
|
13
|
+
module TwitterBackup
|
14
|
+
def self.prepare_file file
|
15
|
+
file = File.expand_path file
|
16
|
+
dir = File.dirname file
|
17
|
+
FileUtils.mkdir_p dir unless File.exist? dir
|
18
|
+
FileUtils.touch file unless File.exist? file
|
19
|
+
raise( Error::InvalidPath, "Can't create file: #{file}" ) unless File.exist?( file )
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
unless Kernel.respond_to?(:require_relative)
|
24
|
+
module Kernel
|
25
|
+
def require_relative(path)
|
26
|
+
require File.join(File.dirname(caller[0]), path.to_str)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
class File
|
31
|
+
def tail(n)
|
32
|
+
buffer = 1024
|
33
|
+
idx = (size - buffer).abs
|
34
|
+
chunks = []
|
35
|
+
lines = 0
|
36
|
+
begin
|
37
|
+
seek(idx)
|
38
|
+
chunk = read(buffer)
|
39
|
+
lines += chunk ? chunk.count("\n") : 0
|
40
|
+
chunks.unshift chunk
|
41
|
+
idx -= buffer
|
42
|
+
end while lines < ( n + 1 ) && pos != 0
|
43
|
+
tail_of_file = chunks.join('')
|
44
|
+
ary = tail_of_file.split(/\n/)
|
45
|
+
lines_to_return = ary[ ary.size - n, ary.size - 1 ] || []
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
|
2
|
+
CONFIG_DIR = File.expand_path(File.join("~/", ".config", "twitter_backup"))
|
3
|
+
CONFIG_FILE = File.join(CONFIG_DIR, "config.yml")
|
4
|
+
|
5
|
+
CONFIG_DEFAULTS = {
|
6
|
+
:credentials => {
|
7
|
+
:consumer_key => "",
|
8
|
+
:consumer_secret => "",
|
9
|
+
:oauth_token => "",
|
10
|
+
:oauth_token_secret => "",
|
11
|
+
},
|
12
|
+
:db => {
|
13
|
+
:adapter => "sqlite3",
|
14
|
+
:database => File.join(CONFIG_DIR, "tweets.sqlite3"),
|
15
|
+
:pool => 5,
|
16
|
+
:timeout => 5000
|
17
|
+
},
|
18
|
+
:backup_file => ""
|
19
|
+
}
|
20
|
+
|
21
|
+
module TwitterBackup
|
22
|
+
class TBConfig
|
23
|
+
class << self
|
24
|
+
def load
|
25
|
+
TwitterBackup.prepare_file config_file
|
26
|
+
yaml = YAML::load_file( config_file )
|
27
|
+
@@options = if yaml.is_a? Hash
|
28
|
+
CONFIG_DEFAULTS.merge(yaml)
|
29
|
+
else
|
30
|
+
CONFIG_DEFAULTS
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def save options
|
35
|
+
@@options = options
|
36
|
+
TwitterBackup.prepare_file config_file
|
37
|
+
File.open( config_file, "w" ){|f| YAML::dump( options, f )}
|
38
|
+
end
|
39
|
+
|
40
|
+
def options
|
41
|
+
@@options ||= load
|
42
|
+
end
|
43
|
+
|
44
|
+
def credentials_missing?
|
45
|
+
options[:credentials].values.map(&:strip).include? ""
|
46
|
+
end
|
47
|
+
|
48
|
+
def path_to_backup_defined?
|
49
|
+
options[:backup_file].present?
|
50
|
+
end
|
51
|
+
|
52
|
+
def save_backup_file file
|
53
|
+
options[:backup_file] = file
|
54
|
+
save options
|
55
|
+
end
|
56
|
+
|
57
|
+
def save_credentials new_credentials
|
58
|
+
options[:credentials] = options[:credentials].merge(new_credentials)
|
59
|
+
save options
|
60
|
+
end
|
61
|
+
|
62
|
+
def empty_credentials
|
63
|
+
new_credentials = { :consumer_key => "",
|
64
|
+
:consumer_secret => "",
|
65
|
+
:oauth_token => "",
|
66
|
+
:oauth_token_secret => ""}
|
67
|
+
save_credentials new_credentials
|
68
|
+
end
|
69
|
+
|
70
|
+
def configure_database
|
71
|
+
ActiveRecord::Base.establish_connection(options[:db])
|
72
|
+
unless ActiveRecord::Base.connection.tables.include?("tweets")
|
73
|
+
ActiveRecord::Migration.create_table :tweets do |t|
|
74
|
+
t.text :status
|
75
|
+
t.integer :status_id
|
76
|
+
t.timestamp :created_at
|
77
|
+
t.text :raw
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def configure_twitter_gem
|
83
|
+
raise MissingCredentials if credentials_missing?
|
84
|
+
Twitter.configure do |config|
|
85
|
+
config.consumer_key = options[:credentials][:consumer_key]
|
86
|
+
config.consumer_secret = options[:credentials][:consumer_secret]
|
87
|
+
config.oauth_token = options[:credentials][:oauth_token]
|
88
|
+
config.oauth_token_secret = options[:credentials][:oauth_token_secret]
|
89
|
+
end
|
90
|
+
@@twitter_gem_configured = true
|
91
|
+
end
|
92
|
+
|
93
|
+
def check_backup_file
|
94
|
+
begin
|
95
|
+
backup_file = File.expand_path(options[:backup_file])
|
96
|
+
TwitterBackup.prepare_file( backup_file ) unless File.exist?( backup_file )
|
97
|
+
rescue Exception => e
|
98
|
+
raise Error::InvalidBackupFile, e.message
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def configure
|
103
|
+
say ".... configuring database" if passed_opts.verbose?
|
104
|
+
configure_database
|
105
|
+
|
106
|
+
say ".... configuring twitter gem" if passed_opts.verbose?
|
107
|
+
configure_twitter_gem
|
108
|
+
|
109
|
+
say ".... checking backup file: #{options[:backup_file]}" if passed_opts.verbose?
|
110
|
+
check_backup_file
|
111
|
+
end
|
112
|
+
|
113
|
+
def verify_credentials
|
114
|
+
say ".... checking validity of credentials" if passed_opts.verbose?
|
115
|
+
configure_twitter_gem unless @@twitter_gem_configured
|
116
|
+
@@user = Twitter.verify_credentials(:skip_status => true, :include_entities => false)
|
117
|
+
end
|
118
|
+
|
119
|
+
def user
|
120
|
+
@@user ||= verify_credentials
|
121
|
+
end
|
122
|
+
|
123
|
+
def config_file
|
124
|
+
passed_opts[:config] || CONFIG_FILE
|
125
|
+
end
|
126
|
+
|
127
|
+
def passed_opts
|
128
|
+
@@opts ||= Slop.parse(:help => true) do
|
129
|
+
on :v, :verbose, 'Enable verbose mode'
|
130
|
+
on :f, :force, "Try to download tweets, even it seems useless"
|
131
|
+
on :s, :seed, "Try to download tweets older then the oldest one you have"
|
132
|
+
on :c, :config, "Config file. Default: #{CONFIG_FILE}", :argument => true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module TwitterBackup
|
2
|
+
class Tweet < ActiveRecord::Base
|
3
|
+
|
4
|
+
serialize :raw
|
5
|
+
|
6
|
+
scope :slim, select([:id, :created_at, :status, :status_id])
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def update_tweets(args={})
|
11
|
+
args = {
|
12
|
+
:from => nil,
|
13
|
+
:to => latest.try(:status_id)
|
14
|
+
}.merge(args)
|
15
|
+
if args[:from] == :earliest
|
16
|
+
args[:from] = earliest.try(:status_id)
|
17
|
+
args[:to] = nil if args[:to] == latest.try(:status_id)
|
18
|
+
end
|
19
|
+
if !synced? || TBConfig.passed_opts.force?
|
20
|
+
say ".... Updating tweets from #{args[:from] || 'the youngest one'} to #{args[:to] || 'the oldest one'}" if TBConfig.passed_opts.verbose?
|
21
|
+
download(:from => args[:from], :to => args[:to]).each do |tweet|
|
22
|
+
if absent_tweet = find_by_status_id(tweet.id)
|
23
|
+
next
|
24
|
+
else
|
25
|
+
say %[<%= color(".", YELLOW) %> ] if TBConfig.passed_opts.verbose?
|
26
|
+
tweet_text = if tweet.retweet?
|
27
|
+
"RT @#{tweet.retweeted_status.user.screen_name}: #{tweet.retweeted_status.text}"
|
28
|
+
else
|
29
|
+
tweet.text
|
30
|
+
end
|
31
|
+
create(
|
32
|
+
:status_id => tweet.id,
|
33
|
+
:status => tweet_text,
|
34
|
+
:created_at => tweet.created_at,
|
35
|
+
:raw => tweet
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
say "!"
|
40
|
+
say ".... Updating succeeded" if TBConfig.passed_opts.verbose?
|
41
|
+
else
|
42
|
+
say ".... Seems like there is nothing we can update" if TBConfig.passed_opts.verbose?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def latest
|
47
|
+
self.slim.order("created_at DESC").first
|
48
|
+
end
|
49
|
+
def earliest
|
50
|
+
self.slim.order("created_at DESC").last
|
51
|
+
end
|
52
|
+
|
53
|
+
def download(args={})
|
54
|
+
args = {
|
55
|
+
:from => nil,
|
56
|
+
:to => nil
|
57
|
+
}.merge(args)
|
58
|
+
args[:from], args[:to] = [args[:from], args[:to]].sort.reverse unless [args[:from], args[:to]].include? nil
|
59
|
+
|
60
|
+
needed_tweets = []
|
61
|
+
|
62
|
+
if args[:from].blank?
|
63
|
+
say ".... user_timeline API request" if TBConfig.passed_opts.verbose?
|
64
|
+
received_tweets = Twitter.user_timeline(:count => 200)
|
65
|
+
else
|
66
|
+
say ".... user_timeline API request" if TBConfig.passed_opts.verbose?
|
67
|
+
received_tweets = Twitter.user_timeline(:count => 200, :max_id => args[:from])
|
68
|
+
end
|
69
|
+
|
70
|
+
if args[:to].blank?
|
71
|
+
id_of_the_earliest_received_tweet = nil
|
72
|
+
until id_of_the_earliest_received_tweet == received_tweets.last.id
|
73
|
+
id_of_the_earliest_received_tweet = received_tweets.last.id
|
74
|
+
say ".... user_timeline API request" if TBConfig.passed_opts.verbose?
|
75
|
+
received_tweets.concat(
|
76
|
+
Twitter.user_timeline(:count => 200, :max_id => received_tweets.last.id)
|
77
|
+
)
|
78
|
+
end
|
79
|
+
received_tweets
|
80
|
+
else args[:to]
|
81
|
+
id_of_the_earliest_needed_tweet = args[:to]
|
82
|
+
until received_tweets.map(&:id).include? id_of_the_earliest_needed_tweet
|
83
|
+
say ".... user_timeline API request" if TBConfig.passed_opts.verbose?
|
84
|
+
received_tweets.concat(
|
85
|
+
Twitter.user_timeline(:count => 200, :max_id => received_tweets.last.id)
|
86
|
+
)
|
87
|
+
end
|
88
|
+
index_of_earliest_needed_tweet = received_tweets.rindex{|tweet| tweet.id == id_of_the_earliest_needed_tweet}
|
89
|
+
received_tweets[0..index_of_earliest_needed_tweet]
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
def synced?
|
95
|
+
self.count == TBConfig.user.statuses_count
|
96
|
+
end
|
97
|
+
|
98
|
+
def dump_to_backup_file
|
99
|
+
if TBConfig.passed_opts.verbose?
|
100
|
+
say ".... Saving tweets to: #{TBConfig.options[:backup_file]}"
|
101
|
+
end
|
102
|
+
tweets = slim.order("created_at DESC").map{ |tweet| {
|
103
|
+
:id => tweet.status_id,
|
104
|
+
:text => tweet.status,
|
105
|
+
:created_at => tweet.created_at,
|
106
|
+
:link => tweet.public_link } }
|
107
|
+
File.open( TBConfig.options[:backup_file], "w" ) { |f| YAML::dump( tweets, f ) }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def public_link
|
112
|
+
"https://twitter.com/#{TBConfig.user.screen_name}/status/#{self.status_id}"
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module TwitterBackup
|
2
|
+
module UI
|
3
|
+
class << self
|
4
|
+
def ask_credentials
|
5
|
+
say "You need to give us necessary credentials"
|
6
|
+
say "Get them at https://dev.twitter.com/apps"
|
7
|
+
|
8
|
+
existing_credentials = TBConfig.options[:credentials]
|
9
|
+
new_credentials = {}
|
10
|
+
|
11
|
+
new_credentials[:consumer_key] = ask("Consumer key? ").to_s if existing_credentials[:consumer_key].blank?
|
12
|
+
new_credentials[:consumer_secret] = ask("Consumer secret? ").to_s if existing_credentials[:consumer_secret].blank?
|
13
|
+
new_credentials[:oauth_token] = ask("Access token? ").to_s if existing_credentials[:oauth_token].blank?
|
14
|
+
new_credentials[:oauth_token_secret] = ask("Access token secret? ").to_s if existing_credentials[:oauth_token_secret].blank?
|
15
|
+
|
16
|
+
TBConfig.save_credentials new_credentials
|
17
|
+
raise MissingCredentials if TBConfig.credentials_missing?
|
18
|
+
end
|
19
|
+
|
20
|
+
def define_path_to_backup
|
21
|
+
default_path = File.join(CONFIG_DIR, "tweets.yml")
|
22
|
+
say "We are going to save your tweets to #{TBConfig.options[:db][:database]}"
|
23
|
+
say "And also as plain text to #{default_path}"
|
24
|
+
if agree ("Dou you want to define another directory for text copy? ")
|
25
|
+
backup_dir = ask("Enter a path to your direcory: ") do |q|
|
26
|
+
q.validate = lambda { |file| File.exist?(File.dirname(File.expand_path(file))) }
|
27
|
+
q.responses[:not_valid] = "You can't save files there"
|
28
|
+
q.confirm = true
|
29
|
+
end
|
30
|
+
backup_file = File.expand_path(File.join(backup_dir, "tweets.yml"))
|
31
|
+
else
|
32
|
+
backup_file = default_path
|
33
|
+
end
|
34
|
+
|
35
|
+
TBConfig.save_backup_file backup_file
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def greet_user
|
40
|
+
say %[.... <%= color("Tweets for: #{TBConfig.user.name}", GREEN) %>!]
|
41
|
+
say %[.... Your tweets at twitter.com: #{TBConfig.user.statuses_count}]
|
42
|
+
end
|
43
|
+
|
44
|
+
def exit_screen
|
45
|
+
if ActiveRecord::Base.connected?
|
46
|
+
say %[.... Your tweets in this backup: #{Tweet.count}]
|
47
|
+
if Tweet.earliest.present?
|
48
|
+
say %[.... Your earliest tweet:]
|
49
|
+
say %[.... <%= color("#{Tweet.earliest.try(:status)}", YELLOW) %>]
|
50
|
+
end
|
51
|
+
if Tweet.latest.present?
|
52
|
+
say %[.... Your latest tweet:]
|
53
|
+
say %[.... <%= color("#{Tweet.latest.try(:status)}", GREEN) %>]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def missing_credentials_exit
|
59
|
+
say %[.... <%= color("We can't work without credentials. Sorry.", RED) %>]
|
60
|
+
say ".... Go get them at https://dev.twitter.com/apps"
|
61
|
+
say ".... And run this script again"
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
|
65
|
+
def wrong_credentials_exit
|
66
|
+
say %[.... <%= color("Your credentials are somehow wrong.", RED) %>]
|
67
|
+
say ".... Go get the right ones at https://dev.twitter.com/apps"
|
68
|
+
say ".... And run this script again"
|
69
|
+
exit 1
|
70
|
+
end
|
71
|
+
|
72
|
+
def failed_backup_path_exit message
|
73
|
+
say %[.... <%= color("We've failed to create #{TBConfig.options[:backup_file]} for you.", RED) %>]
|
74
|
+
say %[.... #{message}]
|
75
|
+
TBConfig.save_backup_file("")
|
76
|
+
say ".... Run this script again and chose another place to store your backup"
|
77
|
+
say ".... Or edit it manually at #{TBConfig.config_file}"
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
|
81
|
+
def too_many_requests_exit
|
82
|
+
say %[.... <%= color("You've exceeded your request limit", RED) %>]
|
83
|
+
say ".... Try again tomorrow =)"
|
84
|
+
exit 1
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/twitter_backup/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["il.zoff"]
|
6
|
+
gem.email = ["il.zoff@gmail.com"]
|
7
|
+
gem.description = %q{This gem will download your tweets from Twitter and save them to sqlite3 database and plaintext (yaml) archive file.}
|
8
|
+
gem.summary = %q{Twitter archiver}
|
9
|
+
gem.homepage = "http://github.com/ilzoff/twitter_backup"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "twitter_backup"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = TwitterBackup::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency("highline")
|
19
|
+
gem.add_dependency("activesupport")
|
20
|
+
gem.add_dependency("activerecord")
|
21
|
+
gem.add_dependency("sqlite3")
|
22
|
+
gem.add_dependency("twitter")
|
23
|
+
gem.add_dependency("slop")
|
24
|
+
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: twitter_backup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- il.zoff
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: highline
|
16
|
+
requirement: !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: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activesupport
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activerecord
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: sqlite3
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: twitter
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: slop
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: This gem will download your tweets from Twitter and save them to sqlite3
|
111
|
+
database and plaintext (yaml) archive file.
|
112
|
+
email:
|
113
|
+
- il.zoff@gmail.com
|
114
|
+
executables:
|
115
|
+
- twitter_backup
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- .gitignore
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- bin/twitter_backup
|
125
|
+
- lib/twitter_backup.rb
|
126
|
+
- lib/twitter_backup/config.rb
|
127
|
+
- lib/twitter_backup/error.rb
|
128
|
+
- lib/twitter_backup/tweet.rb
|
129
|
+
- lib/twitter_backup/ui.rb
|
130
|
+
- lib/twitter_backup/version.rb
|
131
|
+
- twitter_backup.gemspec
|
132
|
+
homepage: http://github.com/ilzoff/twitter_backup
|
133
|
+
licenses: []
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ! '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 1.8.24
|
153
|
+
signing_key:
|
154
|
+
specification_version: 3
|
155
|
+
summary: Twitter archiver
|
156
|
+
test_files: []
|