twitter_backup 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in twitter_backup.gemspec
4
+ gemspec
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.
@@ -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?
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -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,7 @@
1
+ module TwitterBackup
2
+ module Error
3
+ class InvalidPath < StandardError; end
4
+ class InvalidBackupFile < StandardError; end
5
+ class MissingCredentials < StandardError; end
6
+ end
7
+ 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,3 @@
1
+ module TwitterBackup
2
+ VERSION = "0.1.0"
3
+ 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: []