retjilp 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/COPYING +25 -0
- data/README.markdown +25 -0
- data/bin/retjilp +7 -0
- data/config +27 -0
- data/lib/retjilp/options.rb +24 -0
- data/lib/retjilp/retweeter.rb +142 -0
- data/lib/retjilp/runner.rb +14 -0
- data/retjilp.gemspec +22 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2abfa27b54c9c65411d42b2da87ef91861120ac1
|
4
|
+
data.tar.gz: 11990c7cbc4e08b08d845967a1677d8fab55ef0f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 66febc755793570d43e7f8a528d78c54915fe2f160ba1106b4e93b63c0d160f2daefdec204e8889e1db99ec0901ca8449869cc3d6b4e46455cb418fa50452069
|
7
|
+
data.tar.gz: 8159a921b9d8d4046308c00ac44b9cb6f6a16b1ef31deab3704495d082cd5c617aca8802b3ed7d9c843da89987fdf26540cd6555ed097f9dd4461403437f046b
|
data/AUTHORS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Remko Tronçon (http://el-tramo.be)
|
data/COPYING
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2011-2012 Remko Tronçon (http://el-tramo.be)
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
* Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
* Neither the name of the <organization> nor the
|
12
|
+
names of its contributors may be used to endorse or promote products
|
13
|
+
derived from this software without specific prior written permission.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
25
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Retjilp: Native Auto-retweet bot
|
2
|
+
--------------------------------
|
3
|
+
<http://el-tramo.be/blog/retjilp>
|
4
|
+
|
5
|
+
Retjilp logs into your account, scans all the tweets from your following
|
6
|
+
list or another defined list for a set of matching words, and retweets
|
7
|
+
the ones that match (using the native retweet API).
|
8
|
+
|
9
|
+
Prerequisites of this script are Ruby and the `oauth` and `json_pure`
|
10
|
+
ruby gems (`gem install <gem>`).
|
11
|
+
|
12
|
+
To use this script, you will need to have registered an application with
|
13
|
+
<http://twitter.com/apps> to get a consumer key and secret, and fill these
|
14
|
+
values in the `config` file. After having changed the `config` file, move it
|
15
|
+
to a dir `.retjilp` in your homedir (i.e. `~/.retjilp`).
|
16
|
+
|
17
|
+
To start the script, run
|
18
|
+
|
19
|
+
./retjilp.rb
|
20
|
+
|
21
|
+
To get a list of command-line parameters, use the `--help` option.
|
22
|
+
|
23
|
+
The first time the script is run, it will ask you to authorize the application
|
24
|
+
in your twitter account. After this is done, the script will automatically log
|
25
|
+
in the next time it is run.
|
data/bin/retjilp
ADDED
data/config
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
/*
|
2
|
+
* Retjilp configuration file.
|
3
|
+
*
|
4
|
+
* Change this to reflect your setup, and put it in ~/.retjilp
|
5
|
+
*/
|
6
|
+
{
|
7
|
+
/*
|
8
|
+
* Consumer key and secret.
|
9
|
+
* Get this by registering a new (desktop) application at
|
10
|
+
* http://twitter.com/apps
|
11
|
+
*/
|
12
|
+
"consumer_key": "abcdeFghIjklMnOpQrStUv",
|
13
|
+
"consumer_secret": "abcdefgh123456789abcdefgh123456789abcdefg",
|
14
|
+
|
15
|
+
/*
|
16
|
+
* The strings that a tweet should be matched against.
|
17
|
+
* These strings are matched in lower case.
|
18
|
+
*/
|
19
|
+
"match": ["#sometag", "#someothertag", "someword"]
|
20
|
+
|
21
|
+
/*
|
22
|
+
* List name from which statuses are retweeted.
|
23
|
+
* Set this config value if you want to retweet only from
|
24
|
+
* this list instead of your following list.
|
25
|
+
*/
|
26
|
+
/* "retweet_from_list": "auto-retweet" */
|
27
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Retjilp
|
5
|
+
class Options
|
6
|
+
attr_reader :log_level
|
7
|
+
|
8
|
+
def initialize(argv)
|
9
|
+
@log_level = Logger::WARN
|
10
|
+
OptionParser.new do |opts|
|
11
|
+
opts.banner = "Usage: retjilp [ --help ] [ --verbose | --debug ]"
|
12
|
+
opts.on("--verbose", "Run with verbose output") { @log_level = Logger::INFO }
|
13
|
+
opts.on("--debug", "Run with debug output") { @log_level = Logger::DEBUG }
|
14
|
+
opts.on_tail("-h", "--help", "Show this help") { puts opts ; exit }
|
15
|
+
begin
|
16
|
+
opts.parse!(argv)
|
17
|
+
rescue => e
|
18
|
+
STDERR.puts e.message, "\n", opts
|
19
|
+
exit(-1)
|
20
|
+
end
|
21
|
+
end.parse!(argv)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'oauth'
|
3
|
+
require 'json/pure'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
require_relative 'options'
|
7
|
+
|
8
|
+
module Retjilp
|
9
|
+
TWITTER_URI = "http://api.twitter.com"
|
10
|
+
API_VERSION = "1.1"
|
11
|
+
|
12
|
+
class Retweeter
|
13
|
+
def initialize(options)
|
14
|
+
@log = Logger.new(STDERR)
|
15
|
+
@log.formatter = proc { |severity, time, prog, msg| "#{severity}: #{msg}\n" }
|
16
|
+
@log.level = options.log_level
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
# Initialize data dir
|
21
|
+
@log.info("Reading configuration file")
|
22
|
+
data_dir = File.expand_path("~/.retjilp")
|
23
|
+
config_filename = File.join(data_dir, "config")
|
24
|
+
access_token_filename = File.join(data_dir, "access_token")
|
25
|
+
|
26
|
+
# Read configuration file
|
27
|
+
begin
|
28
|
+
config = File.open(config_filename) { |f| JSON.load(f) }
|
29
|
+
rescue => e
|
30
|
+
fatal_exit("Error parsing configuration file #{config_filename}: #{e.message}")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Initialize the access token
|
34
|
+
access_token = nil
|
35
|
+
user_info = nil
|
36
|
+
if File.exist?(access_token_filename)
|
37
|
+
# Try using the cached token
|
38
|
+
@log.info("Loading cached access token from #{access_token_filename}")
|
39
|
+
File.open(access_token_filename) do |f|
|
40
|
+
begin
|
41
|
+
access_token_data = JSON.load(f)
|
42
|
+
consumer = OAuth::Consumer.new(config["consumer_key"], config["consumer_secret"], { :site => TWITTER_URI })
|
43
|
+
access_token = OAuth::AccessToken.new(consumer, access_token_data["token"], access_token_data["secret"])
|
44
|
+
unless user_info = verify_token(access_token)
|
45
|
+
@log.warn("Cached token not authorized")
|
46
|
+
access_token = nil
|
47
|
+
end
|
48
|
+
rescue JSON::ParserError
|
49
|
+
@log.warn("Cached token does not parse")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Request the token if the cached access token does not exist
|
55
|
+
unless access_token
|
56
|
+
STDIN.tty? or fatal_exit("This script must be run interactively the first time to be able to authenticate.")
|
57
|
+
@log.info("Requesting new access token")
|
58
|
+
consumer = OAuth::Consumer.new(
|
59
|
+
config["consumer_key"],
|
60
|
+
config["consumer_secret"],
|
61
|
+
:site => TWITTER_URI,
|
62
|
+
:scheme => :header,
|
63
|
+
:request_token_path => "/oauth/request_token",
|
64
|
+
:authorize_path => "/oauth/authorize",
|
65
|
+
:access_token_path => "/oauth/access_token",
|
66
|
+
:http_method => :post)
|
67
|
+
request_token = consumer.get_request_token(:oauth_callback => "oob")
|
68
|
+
|
69
|
+
puts "Please open #{request_token.authorize_url} in your browser, authorize Retjilp, and enter the PIN code below:"
|
70
|
+
verifier = STDIN.gets.chomp
|
71
|
+
|
72
|
+
begin
|
73
|
+
access_token = request_token.get_access_token(:oauth_verifier => verifier)
|
74
|
+
rescue OAuth::Unauthorized
|
75
|
+
fatal_exit("Invalid PIN verification!")
|
76
|
+
end
|
77
|
+
user_info = verify_token(access_token) or fatal_exit("Access token not authorized!")
|
78
|
+
@log.info("Caching token in #{access_token_filename}")
|
79
|
+
File.open(access_token_filename, 'w+') do |f|
|
80
|
+
access_token_data = {
|
81
|
+
"token" => access_token.token,
|
82
|
+
"secret" => access_token.secret
|
83
|
+
}
|
84
|
+
JSON.dump(access_token_data, f)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
@log.info("Logged in as #{user_info["screen_name"]}")
|
89
|
+
|
90
|
+
# Get a list of retweeted ids
|
91
|
+
@log.info("Fetching retweets")
|
92
|
+
retweets = JSON.parse(access_token.get("/#{API_VERSION}/statuses/user_timeline.json?trim_user=true&include_rts=true").body)
|
93
|
+
@log.debug(JSON.pretty_generate(retweets))
|
94
|
+
not retweets.include? "error" or fatal_exit("Error fetching retweets: #{retweets}")
|
95
|
+
|
96
|
+
retweeted_ids = retweets.map { |retweet| retweet["retweeted_status"]["id"] }.sort!
|
97
|
+
|
98
|
+
# Fetch the statuses
|
99
|
+
@log.info("Fetching friends statuses")
|
100
|
+
if config["retweet_from_list"]
|
101
|
+
status_uri = "/#{API_VERSION}/lists/statuses.json?slug=#{config["retweet_from_list"]}&owner_screen_name=#{user_info["screen_name"]}&include_rts=true"
|
102
|
+
else
|
103
|
+
status_uri = "/#{API_VERSION}/statuses/home_timeline.json?trim_user=true"
|
104
|
+
end
|
105
|
+
status_uri += "&since_id=#{retweeted_ids[0]}" unless retweeted_ids.empty?
|
106
|
+
statuses = JSON.parse(access_token.get(status_uri).body)
|
107
|
+
@log.debug(JSON.pretty_generate(statuses))
|
108
|
+
not statuses.include? "error" or fatal_exit("Error fetching statuses: #{statuses.to_s}")
|
109
|
+
|
110
|
+
# Retweet statuses
|
111
|
+
statuses.each do |status|
|
112
|
+
should_retweet = (config["match"].empty? or config["match"].any? { |match|
|
113
|
+
status["text"].downcase.include? match.downcase
|
114
|
+
})
|
115
|
+
if should_retweet
|
116
|
+
id_to_retweet = status.has_key?("retweeted_status") ? status["retweeted_status"]["id"] : status["id"]
|
117
|
+
if retweeted_ids.include? id_to_retweet
|
118
|
+
@log.debug("Already retweeted: #{status["text"]}")
|
119
|
+
else
|
120
|
+
@log.info("Retweeting: #{status["text"]}")
|
121
|
+
result = access_token.post("/#{API_VERSION}/statuses/retweet/#{id_to_retweet}.json")
|
122
|
+
result.class == Net::HTTPOK or @log.error("Error retweeting #{result.body}")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
# Print a log message, and exit
|
130
|
+
def fatal_exit(msg)
|
131
|
+
@log.fatal(msg)
|
132
|
+
exit -1
|
133
|
+
end
|
134
|
+
|
135
|
+
# Helper method to verify the validity of an access token.
|
136
|
+
# Returns the user info if the token verified correctly.
|
137
|
+
def verify_token(token)
|
138
|
+
response = token.get("/#{API_VERSION}/account/verify_credentials.json")
|
139
|
+
response.class == Net::HTTPOK ? JSON.parse(response.body) : nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/retjilp.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'retjilp'
|
5
|
+
s.summary = 'Automatically retweet tweets'
|
6
|
+
s.description = 'Retjilp logs into your account, scans all the tweets from your following list or another defined list for a set of matching words, and retweets the ones that match (using the native retweet API).'
|
7
|
+
s.requirements = ['']
|
8
|
+
s.version = '0.2'
|
9
|
+
s.author = 'Remko Tronçon'
|
10
|
+
s.email = 'remko@el-tramo.be'
|
11
|
+
s.homepage = 'http://el-tramo.be/blog/retjilp'
|
12
|
+
s.platform = Gem::Platform::RUBY
|
13
|
+
s.required_ruby_version = '>=1.8'
|
14
|
+
s.files = Dir['**/**']
|
15
|
+
s.executables = 'retjilp'
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
s.has_rdoc = false
|
18
|
+
s.license = 'BSD'
|
19
|
+
|
20
|
+
s.add_runtime_dependency('oauth')
|
21
|
+
s.add_runtime_dependency('json_pure')
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: retjilp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.2'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Remko Tronçon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: oauth
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json_pure
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Retjilp logs into your account, scans all the tweets from your following
|
42
|
+
list or another defined list for a set of matching words, and retweets the ones
|
43
|
+
that match (using the native retweet API).
|
44
|
+
email: remko@el-tramo.be
|
45
|
+
executables:
|
46
|
+
- retjilp
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- AUTHORS
|
51
|
+
- bin/retjilp
|
52
|
+
- config
|
53
|
+
- COPYING
|
54
|
+
- lib/retjilp/options.rb
|
55
|
+
- lib/retjilp/retweeter.rb
|
56
|
+
- lib/retjilp/runner.rb
|
57
|
+
- README.markdown
|
58
|
+
- retjilp.gemspec
|
59
|
+
homepage: http://el-tramo.be/blog/retjilp
|
60
|
+
licenses:
|
61
|
+
- BSD
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '1.8'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements:
|
78
|
+
- ''
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.0.0
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Automatically retweet tweets
|
84
|
+
test_files: []
|