twitter_bot 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2008-05-14
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Integrum Technologies, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,36 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ PostInstall.txt
5
+ README.txt
6
+ Rakefile
7
+ config/hoe.rb
8
+ config/requirements.rb
9
+ lib/jabber_bot.rb
10
+ lib/twitter_bot.rb
11
+ lib/twitter_bot/version.rb
12
+ rails_generators/twitter_bot/USAGE
13
+ rails_generators/twitter_bot/templates/awesome_twitter_bot.rb
14
+ rails_generators/twitter_bot/templates/twitter_bot
15
+ rails_generators/twitter_bot/templates/twitter_bot.yml
16
+ rails_generators/twitter_bot/templates/twitter_bot_runner.rb
17
+ rails_generators/twitter_bot/twitter_bot_generator.rb
18
+ script/console
19
+ script/destroy
20
+ script/generate
21
+ script/txt2html
22
+ setup.rb
23
+ spec/spec.opts
24
+ spec/spec_helper.rb
25
+ spec/twitter_bot_spec.rb
26
+ tasks/deployment.rake
27
+ tasks/environment.rake
28
+ tasks/rspec.rake
29
+ tasks/website.rake
30
+ test/test_generator_helper.rb
31
+ test/test_twitter_bot_generator.rb
32
+ website/index.html
33
+ website/index.txt
34
+ website/javascripts/rounded_corners_lite.inc.js
35
+ website/stylesheets/screen.css
36
+ website/template.html.erb
data/PostInstall.txt ADDED
@@ -0,0 +1 @@
1
+ For more information on twitter_bot, see http://integrum.rubyforge.org
data/README.txt ADDED
@@ -0,0 +1,44 @@
1
+ = twitter_bot
2
+
3
+ * http://integrum.rubyforge.org
4
+
5
+ == DESCRIPTION:
6
+
7
+ Enables adding twitter to your app. Uses Twitter's Jabber interface.
8
+
9
+ == SYNOPSIS:
10
+
11
+ script/generate twitter_bot name
12
+ name - the name of your twitter bot
13
+
14
+ 1. Edit config/twitter_bot.yml to add authentication information
15
+ 2. Edit lib/<name>_twitter_bot.rb to specify callback actions
16
+
17
+ == INSTALL:
18
+
19
+ * gem install twitter_bot
20
+
21
+ == LICENSE:
22
+
23
+ (The MIT License)
24
+
25
+ Copyright (c) 2008 Integrum Technologies, LLC
26
+
27
+ Permission is hereby granted, free of charge, to any person obtaining
28
+ a copy of this software and associated documentation files (the
29
+ 'Software'), to deal in the Software without restriction, including
30
+ without limitation the rights to use, copy, modify, merge, publish,
31
+ distribute, sublicense, and/or sell copies of the Software, and to
32
+ permit persons to whom the Software is furnished to do so, subject to
33
+ the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be
36
+ included in all copies or substantial portions of the Software.
37
+
38
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
39
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
41
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
42
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
43
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
44
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/config/hoe.rb ADDED
@@ -0,0 +1,75 @@
1
+ require 'twitter_bot/version'
2
+
3
+ AUTHOR = "Jade Meskill, Curtis Miller, Curtis Edmond" # can also be an array of Authors
4
+ EMAIL = "info@integrumtech.com"
5
+ DESCRIPTION = "Twitter integration for your app using the Twitter Jabber bot"
6
+ GEM_NAME = 'twitter_bot' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'integrum' # The unix name for your project
8
+ HOMEPATH = "http://integrum.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/integrum/twitter_bot"
10
+ EXTRA_DEPENDENCIES = [
11
+ ['daemons', '>= 1.0.9'],
12
+ ['twitter', '>= 0.2.6'],
13
+ ['xmpp4r', '>= 0.3.2']
14
+ ]
15
+
16
+ @config_file = "~/.rubyforge/user-config.yml"
17
+ @config = nil
18
+ RUBYFORGE_USERNAME = "unknown"
19
+ def rubyforge_username
20
+ unless @config
21
+ begin
22
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
23
+ rescue
24
+ puts <<-EOS
25
+ ERROR: No rubyforge config file found: #{@config_file}
26
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
27
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
28
+ EOS
29
+ exit
30
+ end
31
+ end
32
+ RUBYFORGE_USERNAME.replace @config["username"]
33
+ end
34
+
35
+
36
+ REV = nil
37
+ # UNCOMMENT IF REQUIRED:
38
+ # REV = YAML.load(`svn info`)['Revision']
39
+ VERS = TwitterBot::VERSION::STRING + (REV ? ".#{REV}" : "")
40
+ RDOC_OPTS = ['--quiet', '--title', 'twitter_bot documentation',
41
+ "--opname", "index.html",
42
+ "--line-numbers",
43
+ "--main", "README",
44
+ "--inline-source"]
45
+
46
+ class Hoe
47
+ def extra_deps
48
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
49
+ @extra_deps
50
+ end
51
+ end
52
+
53
+ # Generate all the Rake tasks
54
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
55
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
56
+ p.developer(AUTHOR, EMAIL)
57
+ p.description = DESCRIPTION
58
+ p.summary = DESCRIPTION
59
+ p.url = HOMEPATH
60
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
61
+ p.test_globs = ["test/**/test_*.rb"]
62
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
63
+
64
+ # == Optional
65
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
66
+ p.extra_deps = EXTRA_DEPENDENCIES
67
+
68
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
69
+ end
70
+
71
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
72
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
73
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
74
+ $hoe.rsync_args = '-av --delete --ignore-errors'
75
+ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
data/lib/jabber_bot.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'xmpp4r/client'
2
+
3
+ class JabberBot < Jabber::Client
4
+ include Jabber
5
+
6
+ def initialize(jabber_id, jabber_password, options = {})
7
+ @jabber_id = jabber_id
8
+ @jabber_password = jabber_password
9
+ @options = options
10
+
11
+ jid = JID::new(@jabber_id)
12
+ super(jid)
13
+ end
14
+
15
+ def on_message(from, body)
16
+ raise "on_message not implemented!"
17
+ end
18
+
19
+ def connect_and_authenticate
20
+ connect
21
+ auth(@jabber_password)
22
+ send(Presence::new)
23
+ end
24
+
25
+ def run
26
+ main = Thread.current
27
+ add_message_callback { |message|
28
+ next if message.type == :error or message.body.blank?
29
+ # support exit command if owner is set
30
+ main.wakeup if @options[:owner_jabber_id] == message.from && message.body == 'exit'
31
+ # simple callback
32
+ self.on_message(message.from, message.body, message.first_element('entry'))
33
+ }
34
+ Thread.stop
35
+ close
36
+ end
37
+
38
+ def say(jabber_id, body)
39
+ send(Message::new(jabber_id, body).set_type(:chat).set_id('1'))
40
+ end
41
+ end
@@ -0,0 +1,151 @@
1
+ require File.dirname(__FILE__) + '/../lib/jabber_bot'
2
+ require 'twitter'
3
+
4
+ class TwitterBot < JabberBot
5
+ TWITTER_JABBER_ID = 'twitter@twitter.com'
6
+ attr_accessor :track_phrases
7
+
8
+ CONFIG_FILE = File.join(RAILS_ROOT, 'config', 'twitter_bot.yml')
9
+
10
+ def initialize
11
+ raise "File does not exist: '#{CONFIG_FILE}'" unless File.exist?(CONFIG_FILE)
12
+
13
+ twitter_bot_config = YAML.load(File.read(CONFIG_FILE))
14
+ twitter_config = twitter_bot_config['twitter']
15
+ jabber_config = twitter_bot_config['jabber']
16
+
17
+ raise "Please specify the twitter config in '#{CONFIG_FILE}'" if twitter_config.blank?
18
+ raise "Please specify the jabber config in '#{CONFIG_FILE}'" if jabber_config.blank?
19
+
20
+ @twitter_username = twitter_config['username']
21
+ @twitter_password = twitter_config['password']
22
+ jabber_id = jabber_config['id']
23
+ jabber_password = jabber_config['password']
24
+
25
+ raise "The twitter username has not been specified in '#{CONFIG_FILE}'" if @twitter_username.blank?
26
+ raise "The twitter password has not been specified in '#{CONFIG_FILE}'" if @twitter_password.blank?
27
+ raise "The jabber ID has not been specified in '#{CONFIG_FILE}'" if jabber_id.blank?
28
+ raise "The jabber password has not been specified in '#{CONFIG_FILE}'" if jabber_password.blank?
29
+
30
+ @track_phrases = twitter_bot_config['track'] || []
31
+ @followers = []
32
+
33
+ @twitter = Twitter::Base.new(@twitter_username, @twitter_password)
34
+ super(jabber_id, jabber_password)
35
+ end
36
+
37
+ def on_directed_tweet(&block); @on_directed_tweet_callback = block; end
38
+ def on_tweet(&block); @on_tweet_callback = block; end
39
+ def on_track(&block); @on_track_callback = block; end
40
+ def on_follow(&block); @on_follow_callback = block; end
41
+ def on_unfollow(&block); @on_unfollow_callback = block; end
42
+
43
+ def create_twitter_entry(entry)
44
+ return if entry.nil?
45
+ xml = Hpricot.XML(entry.to_s)
46
+ Twitter::Status.new do |s|
47
+ s.id = xml.at('entry/id').innerHTML.split('/').last
48
+ s.created_at = Time.parse(xml.at('entry/published').innerHTML)
49
+ s.text = body
50
+ s.user = Twitter.User.new do |u|
51
+ u.screen_name = xml.at('author/name').innerHTML
52
+ u.url = xml.at('source/link')['href']
53
+ u.profile_image_url = xml.at('source/icon').innerHTML
54
+ end
55
+ end
56
+ end
57
+
58
+ def on_message(from, body, entry = nil)
59
+ $stdout.flush
60
+ return unless from == TWITTER_JABBER_ID
61
+ entry = create_twitter_entry(entry) rescue nil
62
+
63
+ callback = @on_tweet_callback
64
+ twitter_message = body.split(': ')
65
+ twitter_body = twitter_message[1,twitter_message.size].join(': ')
66
+ twitter_from = twitter_message.first
67
+
68
+ if twitter_from =~ /You aren\'t tracking anything\. Reply with \'track your keywords\' to start\./
69
+ track_phrases.each { |phrase| say("track #{phrase}") }
70
+ else
71
+ if (track_notification = twitter_from.match(/\((.+)\)/))
72
+ twitter_from = track_notification[1] # Make sure that twitter_from gets set properly in case of event + @<username>
73
+ # callback = @on_track_callback
74
+ end
75
+
76
+ callback.call(twitter_from, twitter_body, entry) if callback
77
+ end
78
+ rescue => e
79
+ $stderr.puts("error parsing #{body} (#{e})")
80
+ end
81
+
82
+ def runn(options = {})
83
+ connect_and_authenticate
84
+ follow_all_followers if options[:follow_all_followers]
85
+ say("untrack all")
86
+
87
+ Thread.new do
88
+ followers_watcher(options)
89
+ end
90
+ run
91
+ end
92
+
93
+ def logger
94
+ @@logger ||= Logger.new(File.join(RAILS_ROOT, 'log', 'twitter_bot.log'))
95
+ end
96
+
97
+ def follow(username)
98
+ say("follow #{username}")
99
+ end
100
+
101
+ def say(line)
102
+ super(TWITTER_JABBER_ID, line)
103
+ end
104
+
105
+ def direct_message(username, message)
106
+ say("d #{username} #{message}")
107
+ end
108
+
109
+ def determine_phrase_for_body(body)
110
+ @track_phrases.each do |phrase|
111
+ phrase_expression = Regexp.new(phrase)
112
+ if phrase_expression.match(body.downcase)
113
+ return phrase
114
+ end
115
+ end
116
+ nil
117
+ end
118
+
119
+ def follow_all_followers
120
+ following = @twitter.friends.collect { |user| user.name }
121
+ @followers = @twitter.followers.collect { |user| user.name }
122
+ @followers.each do |username|
123
+ if !following.index(username)
124
+ follow(username)
125
+ end
126
+ end
127
+ end
128
+
129
+ def followers_watcher(options = {})
130
+ return unless @on_follow_callback || @on_unfollow_callback
131
+ options[:followers_check_wait] ||= 120
132
+ @followers = @twitter.followers.collect { |user| user.name } if @followers.empty?
133
+ while 1 == 1
134
+ twitter_followers = @twitter.followers.collect { |user| user.name }
135
+ twitter_followers.each do |username|
136
+ if !@followers.index(username)
137
+ @on_follow_callback.call(username) if @on_follow_callback
138
+ end
139
+ end
140
+ @followers.each do |username|
141
+ if !twitter_followers.index(username)
142
+ @on_unfollow_callback.call(username) if @on_unfollow_callback
143
+ end
144
+ end
145
+ @followers = twitter_followers
146
+ sleep(options[:followers_check_wait])
147
+ end
148
+ rescue => e
149
+ raise e
150
+ end
151
+ end
@@ -0,0 +1,9 @@
1
+ module TwitterBot #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
File without changes
@@ -0,0 +1,25 @@
1
+ require 'drb'
2
+
3
+ class <%= file_name.classify %>TwitterBot < TwitterBot
4
+ def runn
5
+ self.on_tweet do |username, message, entry|
6
+ logger.info("#{username} said '#{message}'")
7
+ # Add tweet processing code here, not required
8
+ end
9
+
10
+ self.on_follow do |username|
11
+ logger.info("#{username} is following us, will follow #{username} too and send welcome message")
12
+ follow(username)
13
+ self.direct_message(username, "thanks for following me!")
14
+ # Add on follow processing here, not required
15
+ end
16
+
17
+ self.on_unfollow do |username|
18
+ logger.info("#{username} has stopped following us.")
19
+ # Add on unfollow processing here, not required
20
+ end
21
+
22
+ DRb.start_service("druby://:8997", self)
23
+ super
24
+ end
25
+ end