twittbot 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.yardopts +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +37 -0
- data/Rakefile +6 -0
- data/bin/twittbot +5 -0
- data/lib/twittbot.rb +4 -0
- data/lib/twittbot/bot.rb +199 -0
- data/lib/twittbot/botpart.rb +70 -0
- data/lib/twittbot/cli.rb +43 -0
- data/lib/twittbot/defaults.rb +21 -0
- data/lib/twittbot/gem_ext/twitter.rb +2 -0
- data/lib/twittbot/gem_ext/twitter/rest/tweets.rb +7 -0
- data/lib/twittbot/gem_ext/twitter/tweet.rb +45 -0
- data/lib/twittbot/generators/templates/followback/followback.rb +7 -0
- data/lib/twittbot/generators/templates/random_reply/random_reply.rb +21 -0
- data/lib/twittbot/generators/templates/random_tweet/random_tweet.rb +20 -0
- data/lib/twittbot/generators/templates/retweet_bot/retweet_bot.rb +32 -0
- data/lib/twittbot/generators/templates/template_generator.rb +77 -0
- data/lib/twittbot/generators/twittbot/app/app_generator.rb +59 -0
- data/lib/twittbot/generators/twittbot/app/templates/Gemfile +4 -0
- data/lib/twittbot/generators/twittbot/app/templates/README.md +20 -0
- data/lib/twittbot/generators/twittbot/app/templates/_gitignore +2 -0
- data/lib/twittbot/generators/twittbot/app/templates/etc/.keep +0 -0
- data/lib/twittbot/generators/twittbot/app/templates/lib/.keep +0 -0
- data/lib/twittbot/template_lister.rb +22 -0
- data/twittbot.gemspec +28 -0
- metadata +178 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 09e59460e2557a87718a9bad72b57d1b42c2a4c3
|
4
|
+
data.tar.gz: 179a82d6023f5b2900d54841538e1aeb1b673564
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ed8bedb456e2ff5d26857f0758797e20f9ece05bca381e8125381807c0b91168fb20b90263cd162cb2cb6ce0560169c234743d0e07ff38e846df1bf0aa64dc84
|
7
|
+
data.tar.gz: fe77b02ea6703e0b22e8cf8004e10a05a6cae7381fca8120b29a95392cca7934ae4603ea8ab0a23e37a61162534c7da05e957d0345f307b301fb3527a134ebb7
|
data/.gitignore
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Georg G.
|
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,37 @@
|
|
1
|
+
# Twittbot
|
2
|
+
|
3
|
+
Twittbot is the next generation of my old Twitter bot, `twittbot-nd`.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Installation is just as easy as installing the Rubygem:
|
8
|
+
|
9
|
+
$ gem install twittbot
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
Create a new bot:
|
14
|
+
|
15
|
+
$ twittbot new bot-name
|
16
|
+
$ cd bot-name
|
17
|
+
|
18
|
+
Authorize with Twitter:
|
19
|
+
|
20
|
+
$ twittbot auth
|
21
|
+
|
22
|
+
Add a template, such as for a simple reply bot:
|
23
|
+
|
24
|
+
$ twittbot generate random-reply
|
25
|
+
|
26
|
+
`random-reply` is a template that accepts a configuration. You can configure
|
27
|
+
it by editing `./etc/random_reply.yml` with a text editor of your choice.
|
28
|
+
And, if you want to, you can also change its behaviour by editing
|
29
|
+
`./lib/random_reply.rb`.
|
30
|
+
|
31
|
+
To list available templates, try this:
|
32
|
+
|
33
|
+
$ twittbot list-templates
|
34
|
+
|
35
|
+
Finally, run the bot:
|
36
|
+
|
37
|
+
$ twittbot start
|
data/Rakefile
ADDED
data/bin/twittbot
ADDED
data/lib/twittbot.rb
ADDED
data/lib/twittbot/bot.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
require 'twitter'
|
4
|
+
|
5
|
+
require 'twittbot/defaults'
|
6
|
+
require 'twittbot/botpart'
|
7
|
+
require 'twittbot/gem_ext/twitter'
|
8
|
+
|
9
|
+
module Twittbot
|
10
|
+
# Class providing the streaming connections and callback logic for the bot.
|
11
|
+
class Bot
|
12
|
+
|
13
|
+
include Thor::Shell
|
14
|
+
|
15
|
+
def initialize(options = {})
|
16
|
+
@options = {
|
17
|
+
current_dir: FileUtils.pwd
|
18
|
+
}
|
19
|
+
|
20
|
+
$bot = {
|
21
|
+
callbacks: {},
|
22
|
+
config: YAML.load_file(File.expand_path("./#{Twittbot::CONFIG_FILE_NAME}", @options[:current_dir])),
|
23
|
+
periodic: []
|
24
|
+
}.merge!(options)
|
25
|
+
|
26
|
+
load_bot_code
|
27
|
+
end
|
28
|
+
|
29
|
+
# Authenticates an account with Twitter.
|
30
|
+
def auth
|
31
|
+
require 'oauth'
|
32
|
+
say "This will reset your current access tokens.", :red if already_authed?
|
33
|
+
|
34
|
+
# get the request token URL
|
35
|
+
callback = OAuth::OUT_OF_BAND
|
36
|
+
consumer = OAuth::Consumer.new $bot[:config][:consumer_key],
|
37
|
+
$bot[:config][:consumer_secret],
|
38
|
+
site: Twitter::REST::Client::BASE_URL,
|
39
|
+
scheme: :header
|
40
|
+
request_token = consumer.get_request_token(oauth_callback: callback)
|
41
|
+
url = request_token.authorize_url(oauth_callback: callback)
|
42
|
+
|
43
|
+
puts "Open this URL in a browser: #{url}"
|
44
|
+
pin = ''
|
45
|
+
until pin =~ /^\d+$/
|
46
|
+
print "Enter PIN =>"
|
47
|
+
pin = $stdin.gets.strip
|
48
|
+
end
|
49
|
+
|
50
|
+
access_token = request_token.get_access_token(oauth_verifier: pin)
|
51
|
+
$bot[:config][:access_token] = access_token.token
|
52
|
+
$bot[:config][:access_token_secret] = access_token.secret
|
53
|
+
|
54
|
+
# get the bot's user name (screen_name) and print it to the console
|
55
|
+
$bot[:config][:screen_name] = get_screen_name access_token
|
56
|
+
puts "Hello, #{$bot[:config][:screen_name]}!"
|
57
|
+
|
58
|
+
save_config
|
59
|
+
end
|
60
|
+
|
61
|
+
# Starts the bot.
|
62
|
+
def start
|
63
|
+
check_config
|
64
|
+
$bot[:client] ||= Twitter::REST::Client.new do |cfg|
|
65
|
+
cfg.consumer_key = $bot[:config][:consumer_key]
|
66
|
+
cfg.consumer_secret = $bot[:config][:consumer_secret]
|
67
|
+
cfg.access_token = $bot[:config][:access_token]
|
68
|
+
cfg.access_token_secret = $bot[:config][:access_token_secret]
|
69
|
+
end
|
70
|
+
|
71
|
+
@streamer ||= Twitter::Streaming::Client.new do |cfg|
|
72
|
+
cfg.consumer_key = $bot[:config][:consumer_key]
|
73
|
+
cfg.consumer_secret = $bot[:config][:consumer_secret]
|
74
|
+
cfg.access_token = $bot[:config][:access_token]
|
75
|
+
cfg.access_token_secret = $bot[:config][:access_token_secret]
|
76
|
+
end
|
77
|
+
|
78
|
+
@userstream_thread ||= Thread.new do
|
79
|
+
puts "connected to user stream"
|
80
|
+
@streamer.user do |obj|
|
81
|
+
handle_stream_object obj, :user
|
82
|
+
end
|
83
|
+
puts "lost user stream connection"
|
84
|
+
end
|
85
|
+
|
86
|
+
@tweetstream_thread ||= Thread.new do
|
87
|
+
puts "connected to tweet stream"
|
88
|
+
@streamer.filter track: $bot[:config][:track].join(",") do |obj|
|
89
|
+
handle_stream_object obj, :filter
|
90
|
+
end
|
91
|
+
puts "lost tweet stream connection"
|
92
|
+
end
|
93
|
+
|
94
|
+
@periodic_thread ||= Thread.new do
|
95
|
+
loop do
|
96
|
+
do_periodic
|
97
|
+
sleep 60
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@userstream_thread.join
|
102
|
+
@tweetstream_thread.join
|
103
|
+
@periodic_thread.join
|
104
|
+
end
|
105
|
+
|
106
|
+
# Loads the bot's actual code which is stored in the bot's +lib+
|
107
|
+
# subdirectory.
|
108
|
+
def load_bot_code
|
109
|
+
files = Dir["#{File.expand_path('./lib', @options[:current_dir])}/**/*"]
|
110
|
+
files.each do |file|
|
111
|
+
require_relative file.sub(/\.rb$/, '') if file.end_with? '.rb'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Saves the bot's config (i.e. not the botpart ones).
|
116
|
+
def save_config
|
117
|
+
config = $bot[:config].clone
|
118
|
+
config.delete :client
|
119
|
+
File.open "./#{Twittbot::CONFIG_FILE_NAME}", 'w' do |f|
|
120
|
+
f.write config.to_yaml
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Checks some configuration values, e.g. if the bot is already authenticated with Twitter
|
125
|
+
def check_config
|
126
|
+
unless already_authed?
|
127
|
+
say "Please authenticate using `twittbot auth' first.", :red
|
128
|
+
raise 'Not authenticated'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Handles a object yielded from a Twitter::Streaming::Client.
|
133
|
+
# @param object [Object] The object yielded from a Twitter::Streaming::Client connection.
|
134
|
+
# @param type [Symbol] The type of the streamer. Should be either :user or :filter.
|
135
|
+
def handle_stream_object(object, type)
|
136
|
+
opts = {
|
137
|
+
stream_type: type
|
138
|
+
}
|
139
|
+
case object
|
140
|
+
when Twitter::Streaming::FriendList
|
141
|
+
# object: Array with IDs
|
142
|
+
do_callbacks :friend_list, object
|
143
|
+
when Twitter::Tweet
|
144
|
+
# object: Twitter::Tweet
|
145
|
+
is_mention = (object.user.screen_name != $bot[:config][:screen_name] and object.text.include?("@" + $bot[:config][:screen_name]) and not object.retweet?)
|
146
|
+
do_callbacks :retweet, object, opts if object.retweet? and object.retweeted_tweet.user.screen_name == $bot[:config][:screen_name]
|
147
|
+
do_callbacks :mention, object, opts if is_mention
|
148
|
+
do_callbacks :tweet, object, opts.merge({ mention: is_mention, retweet: object.retweet? })
|
149
|
+
when Twitter::Streaming::Event
|
150
|
+
case object.name
|
151
|
+
when :follow, :favorite
|
152
|
+
# :follow -- object: Twitter::Streaming::Event(name: :follow, source: Twitter::User, target: Twitter::User)
|
153
|
+
# :favorite -- object: Twitter::Streaming::Event(name: :favorite, source: Twitter::User, target: Twitter::User, target_object: Twitter::Tweet)
|
154
|
+
do_callbacks object.name, object, opts
|
155
|
+
else
|
156
|
+
puts "no handler for #{object.class.to_s}/#{object.name}\n -- object data:"
|
157
|
+
require 'pp'
|
158
|
+
pp object
|
159
|
+
do_callbacks object.name, object, opts
|
160
|
+
end
|
161
|
+
else
|
162
|
+
puts "no handler for #{object.class.to_s}\n -- object data:"
|
163
|
+
require 'pp'
|
164
|
+
pp object
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Runs callbacks.
|
169
|
+
# @param callback_type [:Symbol] The callback type.
|
170
|
+
# @param object [Object] The object
|
171
|
+
def do_callbacks(callback_type, object, options = {})
|
172
|
+
$bot[:callbacks][callback_type][:block].call object, options unless $bot[:callbacks][callback_type].nil?
|
173
|
+
end
|
174
|
+
|
175
|
+
def do_periodic
|
176
|
+
$bot[:periodic].each_with_index do |h, i|
|
177
|
+
h[:remaining] = if h[:remaining] == 0
|
178
|
+
h[:block].call
|
179
|
+
h[:interval]
|
180
|
+
else
|
181
|
+
h[:remaining] - 1
|
182
|
+
end
|
183
|
+
$bot[:periodic][i] = h
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# @return [Boolean] whether the bot is already authenticated or not.
|
188
|
+
def already_authed?
|
189
|
+
!($bot[:config][:access_token].empty? or $bot[:config][:access_token_secret].empty?)
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
def get_screen_name(access_token)
|
195
|
+
oauth_response = access_token.get('/1.1/account/verify_credentials.json?skip_status=true')
|
196
|
+
oauth_response.body.match(/"screen_name"\s*:\s*"(.*?)"/).captures.first
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'twittbot/defaults'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
module Twittbot
|
5
|
+
class BotPart
|
6
|
+
# @param name [Symbol] The name of the botpart. Should be the same as the file name without the extension.
|
7
|
+
def initialize(name, &block)
|
8
|
+
botpart_config_path = File.expand_path("./etc/#{name}.yml")
|
9
|
+
@config = $bot[:config].merge(if File.exist? botpart_config_path
|
10
|
+
YAML.load_file botpart_config_path
|
11
|
+
else
|
12
|
+
{}
|
13
|
+
end)
|
14
|
+
instance_eval &block
|
15
|
+
end
|
16
|
+
|
17
|
+
# Adds a new callback to +name+.
|
18
|
+
# @param name [Symbol] The callback type.
|
19
|
+
# Can be (at least) one of:
|
20
|
+
#
|
21
|
+
# * :tweet
|
22
|
+
# * :mention
|
23
|
+
# * :retweet
|
24
|
+
# * :favorite
|
25
|
+
# * :friend_list
|
26
|
+
def on(name, *args, &block)
|
27
|
+
$bot[:callbacks][name] ||= {
|
28
|
+
args: args,
|
29
|
+
block: block
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Runs +block+ every +interval+ +unit+(s).
|
34
|
+
# @param interval [Fixnum]
|
35
|
+
# @param unit [Symbol] the time unit.
|
36
|
+
# Can be one of:
|
37
|
+
#
|
38
|
+
# * :minute or :minutes
|
39
|
+
# * :hour or :hours
|
40
|
+
# @param options [Hash] A customizable set of options.
|
41
|
+
# @option options [Boolean] :run_at_start (true) Run the code in +block+ when the bot finished starting.
|
42
|
+
def every(interval, unit = :minutes, options = {}, &block)
|
43
|
+
raise "Not a Fixnum: #{interval}" unless interval.is_a? Fixnum
|
44
|
+
raise "Interval less than 1" if interval < 1
|
45
|
+
|
46
|
+
opts = {
|
47
|
+
run_at_start: true
|
48
|
+
}.merge(options)
|
49
|
+
|
50
|
+
case unit
|
51
|
+
when :min, :mins, :minute, :minutes
|
52
|
+
when :hr, :hrs, :hour, :hours, :horse
|
53
|
+
interval *= 60
|
54
|
+
else
|
55
|
+
raise "Unknown unit: #{unit}"
|
56
|
+
end
|
57
|
+
$bot[:periodic] << {
|
58
|
+
interval: interval,
|
59
|
+
remaining: opts[:run_at_start] ? 0 : interval,
|
60
|
+
block: block
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Twitter::REST::Client]
|
65
|
+
def client
|
66
|
+
$bot[:client]
|
67
|
+
end
|
68
|
+
alias bot client
|
69
|
+
end
|
70
|
+
end
|
data/lib/twittbot/cli.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
require 'twittbot/defaults'
|
4
|
+
|
5
|
+
module Twittbot
|
6
|
+
class CLI < Thor
|
7
|
+
desc 'new APP_NAME', 'Creates a new Twittbot application.'
|
8
|
+
method_option :template_dir, type: :string, aliases: '-t', desc: 'Specifies the template directory to use', default: Twittbot::TEMPLATE_DIR
|
9
|
+
def new(app_name)
|
10
|
+
require 'twittbot/generators/twittbot/app/app_generator'
|
11
|
+
generator = Twittbot::Generators::AppGenerator.new app_name, options
|
12
|
+
generator.create
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'auth', 'authorizes with Twitter'
|
16
|
+
def auth
|
17
|
+
require 'twittbot/bot'
|
18
|
+
bot = Twittbot::Bot.new
|
19
|
+
bot.auth
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'start', 'Starts the bot'
|
23
|
+
def start
|
24
|
+
require 'twittbot/bot'
|
25
|
+
bot = Twittbot::Bot.new
|
26
|
+
bot.start
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'generate TEMPLATE_NAME', 'Installs a template'
|
30
|
+
def generate(template_name)
|
31
|
+
require 'twittbot/generators/templates/template_generator'
|
32
|
+
generator = Twittbot::Generators::TemplateGenerator.new template_name, options
|
33
|
+
generator.create
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'list-templates', 'Lists all templates'
|
37
|
+
def list_templates
|
38
|
+
require 'twittbot/template_lister'
|
39
|
+
lister = Twittbot::TemplateLister.new options
|
40
|
+
lister.list
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Twittbot
|
2
|
+
# The version of Twittbot.
|
3
|
+
VERSION = "0.0.1"
|
4
|
+
|
5
|
+
CONSUMER_KEY = 'FYRuQcDbPAXAyVjuPZMuw' # :nodoc:
|
6
|
+
CONSUMER_SECRET = 'KiLCYTftPdxNebl5DNcj7Ey2Y8YVZu7hfqiFRYkcg' # :nodoc:
|
7
|
+
|
8
|
+
# Contains the path to the initial bot template.
|
9
|
+
TEMPLATE_DIR = File.expand_path '../generators/twittbot/app/templates', __FILE__
|
10
|
+
# The name of the bot's config file.
|
11
|
+
CONFIG_FILE_NAME = 'config.yml'
|
12
|
+
|
13
|
+
# Hash containing the default bot configuration.
|
14
|
+
DEFAULT_BOT_CONFIG = {
|
15
|
+
consumer_key: Twittbot::CONSUMER_KEY,
|
16
|
+
consumer_secret: Twittbot::CONSUMER_SECRET,
|
17
|
+
access_token: '',
|
18
|
+
access_token_secret: '',
|
19
|
+
track: []
|
20
|
+
}
|
21
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Twitter
|
2
|
+
class Tweet
|
3
|
+
# Creates a reply to this tweet.
|
4
|
+
# @param tweet_text [:String] tweet text
|
5
|
+
# @param options [Hash] A customizable set of options.
|
6
|
+
# @option options [Boolean] :reply_all (false) Add all users mentioned in the tweet text to the reply.
|
7
|
+
def reply(tweet_text, options = {})
|
8
|
+
return if $bot.nil? or $bot[:client].nil?
|
9
|
+
opts = {
|
10
|
+
reply_all: false
|
11
|
+
}.merge(options)
|
12
|
+
|
13
|
+
mentions = self.mentioned_users(opts[:reply_all])
|
14
|
+
|
15
|
+
result = "@#{mentions.join(" @")} #{tweet_text}"[(0...140)]
|
16
|
+
|
17
|
+
$bot[:client].update result, in_reply_to_status_id: self.id
|
18
|
+
rescue Twitter::Error => e
|
19
|
+
puts "caught Twitter error while retweeting: #{e.message}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Retweets this tweet.
|
23
|
+
def retweet
|
24
|
+
return if $bot.nil? or $bot[:client].nil?
|
25
|
+
$bot[:client].retweet self.id
|
26
|
+
rescue Twitter::Error => e
|
27
|
+
puts "caught Twitter error while retweeting: #{e.message}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Scans the tweet text for screen names.
|
31
|
+
# @param reply_all [Boolean] Include all users in the reply.
|
32
|
+
# @param screen_name [String] The user's screen name (i.e. that one who clicked "Reply")
|
33
|
+
# @return [Array] An array of user names.
|
34
|
+
def mentioned_users(reply_all = true, screen_name = $bot[:config][:screen_name])
|
35
|
+
userlist = [ self.user.screen_name ]
|
36
|
+
if reply_all
|
37
|
+
self.text.scan /@([A-Za-z0-9_]{1,16})/ do |user_name|
|
38
|
+
user_name = user_name[0]
|
39
|
+
userlist << user_name unless userlist.include?(user_name) or screen_name == user_name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
userlist
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<%
|
2
|
+
# This is the config for this botpart.
|
3
|
+
# It is a hash and will be serialized to YAML and saved to 'etc/template_name.yml'.
|
4
|
+
botpart_config = {
|
5
|
+
replies: [
|
6
|
+
"Yes.",
|
7
|
+
"No.",
|
8
|
+
"Absolutely.",
|
9
|
+
"I don't think so.",
|
10
|
+
"Maybe.",
|
11
|
+
]
|
12
|
+
}
|
13
|
+
%>
|
14
|
+
Twittbot::BotPart.new :<%= @template_name %> do
|
15
|
+
# When someone mentions the bot...
|
16
|
+
on :mention do |tweet|
|
17
|
+
# ... reply to the tweet with a random entry of the :replies list that you
|
18
|
+
# configured and also include every person that was mentioned in the tweet.
|
19
|
+
tweet.reply @config[:replies].sample, reply_all: true
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<%
|
2
|
+
# This is the config for this botpart.
|
3
|
+
# It is a hash and will be serialized to YAML and saved to 'etc/template_name.yml'.
|
4
|
+
botpart_config = {
|
5
|
+
tweets: [
|
6
|
+
"I'm a bot!",
|
7
|
+
"Fun fact: I run on @nilsding's Twittbot. https://github.com/nilsding/twittbot",
|
8
|
+
"My admin is too lazy to change the default values.",
|
9
|
+
"things to do when you're drunk:\n\n1. drink more\n2. repeat step 1",
|
10
|
+
"#{'fox ' * 25}"
|
11
|
+
]
|
12
|
+
}
|
13
|
+
%>
|
14
|
+
Twittbot::BotPart.new :<%= @template_name %> do
|
15
|
+
# Every 15 minutes...
|
16
|
+
every 15, :minutes do
|
17
|
+
# ... tweet a random entry of the :tweets list that you configured
|
18
|
+
bot.tweet @config[:tweets].sample
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<%
|
2
|
+
# This is the config for this botpart.
|
3
|
+
# It is a hash and will be serialized to YAML and saved to 'etc/template_name.yml'.
|
4
|
+
botpart_config = {
|
5
|
+
tags: [
|
6
|
+
'twittbot',
|
7
|
+
'#nilsding'
|
8
|
+
]
|
9
|
+
}
|
10
|
+
|
11
|
+
# Display a message after installation template installation
|
12
|
+
post_install_message <<-MSG
|
13
|
+
Note: This botpart may require some changes to the bot's config:
|
14
|
+
|
15
|
+
If you want to include tweets from users other than those your bot follows,
|
16
|
+
add the same tags you have in your etc/#{@template_name}.yml to the ':track:'
|
17
|
+
section of your #{::Twittbot::CONFIG_FILE_NAME}.
|
18
|
+
MSG
|
19
|
+
%>
|
20
|
+
Twittbot::BotPart.new :<%= @template_name %> do
|
21
|
+
# When there's a new tweet in the timeline...
|
22
|
+
on :tweet do |tweet, opts|
|
23
|
+
# ... check if the tweet text contains at least one of the tags defined in your config...
|
24
|
+
@config[:tags].each do |tag|
|
25
|
+
if tweet.text.downcase.include? tag.downcase
|
26
|
+
# ... and finally retweet it, if the tweet is not a retweet.
|
27
|
+
tweet.retweet unless opts[:retweet]
|
28
|
+
break
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'erubis'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Twittbot
|
6
|
+
module Generators
|
7
|
+
# Class to install a template to the bot.
|
8
|
+
class TemplateGenerator
|
9
|
+
include Thor::Shell
|
10
|
+
|
11
|
+
def initialize(template_name, options)
|
12
|
+
@template_name = template_name.gsub('-', '_')
|
13
|
+
@options = {
|
14
|
+
'template_options' => {}
|
15
|
+
}.merge!(options)
|
16
|
+
@options['template_dir'] = File.expand_path "../#{@template_name}", __FILE__
|
17
|
+
@post_install_messages = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
files = Dir["#{@options['template_dir']}/**/*"]
|
22
|
+
files.each do |file|
|
23
|
+
real_filename = "lib/#{file.sub(/^#{@options['template_dir']}\//, '').sub(/^_/, '.')}"
|
24
|
+
real_path = File.expand_path "./#{real_filename}"
|
25
|
+
|
26
|
+
botpart_config = {}
|
27
|
+
erb = Erubis::Eruby.new File.read(file)
|
28
|
+
final_result = erb.result(binding)
|
29
|
+
|
30
|
+
if File.exist? real_path
|
31
|
+
say_status :exists, real_filename, :red
|
32
|
+
else
|
33
|
+
say_status :create, real_filename, :green
|
34
|
+
if File.directory? file
|
35
|
+
FileUtils.mkdir_p real_path
|
36
|
+
else
|
37
|
+
File.open real_path, 'w' do |f|
|
38
|
+
f.write final_result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
save_config botpart_config, real_filename unless botpart_config.empty?
|
44
|
+
end
|
45
|
+
unless @post_install_messages.empty?
|
46
|
+
say "Post install messages:", :yellow
|
47
|
+
puts @post_install_messages
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Adds an optional post-install message.
|
52
|
+
# @param msg [String] message to display
|
53
|
+
def post_install_message(msg)
|
54
|
+
@post_install_messages << msg
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def save_config(config, path)
|
60
|
+
path = "etc/#{path.sub(/^lib\//, '').sub(/\.rb$/, '.yml')}"
|
61
|
+
real_path = File.expand_path "./#{path}"
|
62
|
+
|
63
|
+
if File.exist? real_path
|
64
|
+
say_status :merge, path, :yellow
|
65
|
+
existing_config = YAML.load_file real_path
|
66
|
+
config.merge! existing_config
|
67
|
+
else
|
68
|
+
say_status :create, path, :green
|
69
|
+
end
|
70
|
+
|
71
|
+
File.open real_path, 'w' do |f|
|
72
|
+
f.write config.to_yaml
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'erubis'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require 'twittbot/defaults'
|
6
|
+
|
7
|
+
module Twittbot
|
8
|
+
module Generators
|
9
|
+
# Class to generate a new Twittbot bot.
|
10
|
+
class AppGenerator
|
11
|
+
include Thor::Shell
|
12
|
+
|
13
|
+
def initialize(app_name, options = {})
|
14
|
+
@options = {
|
15
|
+
'template_dir' => Twittbot::TEMPLATE_DIR
|
16
|
+
}.merge!(options)
|
17
|
+
@app_name = app_name
|
18
|
+
@options['template_dir'] = File.expand_path @options['template_dir']
|
19
|
+
end
|
20
|
+
|
21
|
+
# Creates the bot.
|
22
|
+
def create
|
23
|
+
path = File.expand_path "./#{@app_name}"
|
24
|
+
if File.exist?(@app_name)
|
25
|
+
say "#{File.directory?(@app_name) ? 'Directory' : 'File'} #{@app_name} already exists", :red
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
FileUtils.mkdir_p(path)
|
29
|
+
|
30
|
+
# build the template
|
31
|
+
files = Dir["#{@options['template_dir']}/**/*"]
|
32
|
+
files.each do |file|
|
33
|
+
real_filename = file.sub(/^#{@options['template_dir']}\//, '').sub(/^_/, '.')
|
34
|
+
real_path = "#{path}/#{real_filename}"
|
35
|
+
say_status :create, real_filename, :green
|
36
|
+
if File.directory? file
|
37
|
+
FileUtils.mkdir_p real_path
|
38
|
+
else
|
39
|
+
erb = Erubis::Eruby.new File.read(file)
|
40
|
+
File.open real_path, 'w' do |f|
|
41
|
+
f.write erb.result(binding)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
generate_config path
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def generate_config(base_path)
|
51
|
+
say_status :create, Twittbot::CONFIG_FILE_NAME, :green
|
52
|
+
|
53
|
+
File.open "#{base_path}/#{Twittbot::CONFIG_FILE_NAME}", 'w' do |f|
|
54
|
+
f.write Twittbot::DEFAULT_BOT_CONFIG.to_yaml
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# <%= @app_name %>
|
2
|
+
|
3
|
+
<%= @app_name %> is a bot created using Twittbot <%= ::Twittbot::VERSION %>.
|
4
|
+
|
5
|
+
See [nilsding/twittbot](https://github.com/nilsding/twittbot) to find out
|
6
|
+
more about Twittbot.
|
7
|
+
|
8
|
+
## Installation and usage
|
9
|
+
|
10
|
+
Install this bot's dependencies:
|
11
|
+
|
12
|
+
$ bundle install
|
13
|
+
|
14
|
+
Authorize with Twitter for the first time:
|
15
|
+
|
16
|
+
$ bundle exec twittbot auth
|
17
|
+
|
18
|
+
And finally run the bot:
|
19
|
+
|
20
|
+
$ bundle exec twittbot start
|
File without changes
|
File without changes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Twittbot
|
2
|
+
# Class to list available templates
|
3
|
+
class TemplateLister
|
4
|
+
include Thor::Shell
|
5
|
+
|
6
|
+
# @param options [Hash] The CLI options from Thor
|
7
|
+
def initialize(options)
|
8
|
+
@options = {}.merge(options)
|
9
|
+
@options['templates_dir'] = File.expand_path "../generators/templates", __FILE__
|
10
|
+
end
|
11
|
+
|
12
|
+
# Prints the available templates to stdout.
|
13
|
+
def list
|
14
|
+
dirs = Dir["#{@options['templates_dir']}/*"]
|
15
|
+
dirs.each do |dir|
|
16
|
+
if File.exist? dir and File.directory? dir
|
17
|
+
say File.basename(dir).gsub('_', '-')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/twittbot.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'twittbot/defaults'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'twittbot'
|
8
|
+
spec.version = Twittbot::VERSION
|
9
|
+
spec.authors = ['nilsding']
|
10
|
+
spec.email = ['nilsding@nilsding.org']
|
11
|
+
spec.summary = %q{An advanced Twitter bot.}
|
12
|
+
spec.description = %q{Twittbot is an advanced Twitter bot. See the README for more info.}
|
13
|
+
spec.homepage = 'https://github.com/nilsding/twittbot'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'thor', '~> 0.19'
|
22
|
+
spec.add_dependency 'erubis', '~> 2.7', '>= 2.7.0'
|
23
|
+
spec.add_dependency 'oauth', '~> 0.4'
|
24
|
+
spec.add_dependency 'twitter', '~> 5.14'
|
25
|
+
spec.add_development_dependency 'yard', '~> 0.8'
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: twittbot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- nilsding
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.19'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.19'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: erubis
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.7'
|
34
|
+
- - '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 2.7.0
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.7'
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 2.7.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: oauth
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.4'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.4'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: twitter
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ~>
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '5.14'
|
68
|
+
type: :runtime
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ~>
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '5.14'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: yard
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ~>
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.8'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ~>
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0.8'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: bundler
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ~>
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.7'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ~>
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.7'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: rake
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '10.0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ~>
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '10.0'
|
117
|
+
description: Twittbot is an advanced Twitter bot. See the README for more info.
|
118
|
+
email:
|
119
|
+
- nilsding@nilsding.org
|
120
|
+
executables:
|
121
|
+
- twittbot
|
122
|
+
extensions: []
|
123
|
+
extra_rdoc_files: []
|
124
|
+
files:
|
125
|
+
- .gitignore
|
126
|
+
- .yardopts
|
127
|
+
- Gemfile
|
128
|
+
- LICENSE.txt
|
129
|
+
- README.md
|
130
|
+
- Rakefile
|
131
|
+
- bin/twittbot
|
132
|
+
- lib/twittbot.rb
|
133
|
+
- lib/twittbot/bot.rb
|
134
|
+
- lib/twittbot/botpart.rb
|
135
|
+
- lib/twittbot/cli.rb
|
136
|
+
- lib/twittbot/defaults.rb
|
137
|
+
- lib/twittbot/gem_ext/twitter.rb
|
138
|
+
- lib/twittbot/gem_ext/twitter/rest/tweets.rb
|
139
|
+
- lib/twittbot/gem_ext/twitter/tweet.rb
|
140
|
+
- lib/twittbot/generators/templates/followback/followback.rb
|
141
|
+
- lib/twittbot/generators/templates/random_reply/random_reply.rb
|
142
|
+
- lib/twittbot/generators/templates/random_tweet/random_tweet.rb
|
143
|
+
- lib/twittbot/generators/templates/retweet_bot/retweet_bot.rb
|
144
|
+
- lib/twittbot/generators/templates/template_generator.rb
|
145
|
+
- lib/twittbot/generators/twittbot/app/app_generator.rb
|
146
|
+
- lib/twittbot/generators/twittbot/app/templates/Gemfile
|
147
|
+
- lib/twittbot/generators/twittbot/app/templates/README.md
|
148
|
+
- lib/twittbot/generators/twittbot/app/templates/_gitignore
|
149
|
+
- lib/twittbot/generators/twittbot/app/templates/etc/.keep
|
150
|
+
- lib/twittbot/generators/twittbot/app/templates/lib/.keep
|
151
|
+
- lib/twittbot/template_lister.rb
|
152
|
+
- twittbot.gemspec
|
153
|
+
homepage: https://github.com/nilsding/twittbot
|
154
|
+
licenses:
|
155
|
+
- MIT
|
156
|
+
metadata: {}
|
157
|
+
post_install_message:
|
158
|
+
rdoc_options: []
|
159
|
+
require_paths:
|
160
|
+
- lib
|
161
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - '>='
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
171
|
+
requirements: []
|
172
|
+
rubyforge_project:
|
173
|
+
rubygems_version: 2.4.5
|
174
|
+
signing_key:
|
175
|
+
specification_version: 4
|
176
|
+
summary: An advanced Twitter bot.
|
177
|
+
test_files: []
|
178
|
+
has_rdoc:
|