twittbot 0.0.1
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/.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:
|