4 # :title: Twitter Status Update for rbot
6 # Author:: Carter Parks (carterparks) <carter@carterparks.com>
7 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
8 # Author:: NeoLobster <neolobster@snugglenets.com>
10 # Copyright:: (C) 2007 Carter Parks
11 # Copyright:: (C) 2007 Giuseppe Bilotta
13 # Users can setup their twitter username and password and then begin updating
20 class TwitterPlugin < Plugin
21 Config.register Config::StringValue.new('twitter.key',
23 :desc => "Twitter OAuth Consumer Key")
25 Config.register Config::StringValue.new('twitter.secret',
27 :desc => "Twitter OAuth Consumer Secret")
29 Config.register Config::IntegerValue.new('twitter.status_count',
30 :default => 1, :validate => Proc.new { |v| v > 0 && v <= 10},
31 :desc => "Maximum number of status updates shown by 'twitter status'")
33 Config.register Config::IntegerValue.new('twitter.friends_status_count',
34 :default => 3, :validate => Proc.new { |v| v > 0 && v <= 10},
35 :desc => "Maximum number of status updates shown by 'twitter friends status'")
50 def help(plugin, topic="")
51 return "twitter status [nick] => show nick's (or your) status, use 'twitter friends status [nick]' to also show the friends' timeline | twitter update [status] => updates your status on twitter | twitter authorize => Generates an authorization URL which will give you a PIN to authorize the bot to use your twitter account. | twitter pin [pin] => Finishes bot authorization using the PIN provided by the URL from twitter authorize. | twitter deauthorize => Makes the bot forget your Twitter account. | twitter actions [on|off] => enable/disable twitting of actions (/me does ...)"
54 # update the status on twitter
55 def get_status(m, params)
56 friends = params[:friends]
57 if @registry.has_key?(m.sourcenick + "_access_token")
58 @access_token = YAML::load(@registry[m.sourcenick + "_access_token"])
59 nick = params[:nick] || @access_token.params[:screen_name]
62 m.reply "You are not authorized with Twitter. Please use 'twitter authorize' first to use this feature."
69 m.reply "you should specify the username of the twitter to use, or identify using 'twitter authorize'"
73 count = @bot.config['twitter.friends_status_count']
74 user = URI.escape(nick)
75 if @registry.has_key?(m.sourcenick + "_access_token")
77 #no change to count variable
78 uri = "https://api.twitter.com/1/statuses/friends_timeline.xml?count=#{count}"
79 response = @access_token.get(uri).body
81 count = @bot.config['twitter.status_count']
82 uri = "https://api.twitter.com/1/statuses/user_timeline.xml?screen_name=#{user}&count=#{count}"
83 response = @access_token.get(uri).body
86 #unauthorized user, will try to get from public timeline the old way
87 uri = "http://twitter.com/statuses/user_timeline/#{user}.xml?count=#{count}"
88 response = @bot.httputil.get(uri, :cache => false)
96 rex = REXML::Document.new(response)
97 rex.root.elements.each("status") { |st|
98 # month, day, hour, min, sec, year = st.elements['created_at'].text.match(/\w+ (\w+) (\d+) (\d+):(\d+):(\d+) \S+ (\d+)/)[1..6]
99 # debug [year, month, day, hour, min, sec].inspect
100 # time = Time.local(year.to_i, month, day.to_i, hour.to_i, min.to_i, sec.to_i)
101 time = Time.parse(st.elements['created_at'].text)
103 # Sometimes, time can be in the future; invert the relation in this case
104 delta = ((time > now) ? time - now : now - time)
105 msg = st.elements['text'].to_s + " (#{Utils.secs_to_string(delta.to_i)} ago via #{st.elements['source'].to_s})"
108 author = Utils.decode_html_entities(st.elements['user'].elements['name'].text) + ": " rescue ""
110 texts << author+Utils.decode_html_entities(msg).ircify_html
113 # friends always return the latest 20 updates, so we clip the count
119 m.reply "could not parse status for #{nick}'s friends"
121 m.reply "could not parse status for #{nick}"
125 m.reply texts.reverse.join("\n")
129 rep = "could not get status for #{nick}'s friends"
130 rep << ", try asking in private" unless m.private?
132 rep = "could not get status for #{nick}"
139 def deauthorize(m, params)
140 if @registry.has_key?(m.sourcenick + "_request_token")
141 @registry.delete(m.sourcenick + "_request_token")
143 if @registry.has_key?(m.sourcenick + "_access_token")
144 @registry.delete(m.sourcenick + "_access_token")
146 m.reply "Done! You can reauthorize this account in the future by using 'twitter authorize'"
149 def authorize(m, params)
150 #remove all old authorization data
151 if @registry.has_key?(m.sourcenick + "_request_token")
152 @registry.delete(m.sourcenick + "_request_token")
154 if @registry.has_key?(m.sourcenick + "_access_token")
155 @registry.delete(m.sourcenick + "_access_token")
158 key = @bot.config['twitter.key']
159 secret = @bot.config['twitter.secret']
160 @consumer = OAuth::Consumer.new(key, secret, {
161 :site => "https://api.twitter.com",
162 :request_token_path => "/oauth/request_token",
163 :access_token_path => "/oauth/access_token",
164 :authorize_path => "/oauth/authorize"
166 @request_token = @consumer.get_request_token
167 @registry[m.sourcenick + "_request_token"] = YAML::dump(@request_token)
168 m.reply "Go to this URL to get your authorization PIN, then use 'twitter pin <pin>' to finish authorization: " + @request_token.authorize_url
172 unless @registry.has_key?(m.sourcenick + "_request_token")
173 m.reply "You must first use twitter authorize to get an authorization URL, which you can use to get a PIN for me to use to verify your Twitter account"
176 @request_token = YAML::load(@registry[m.sourcenick + "_request_token"])
178 @access_token = @request_token.get_access_token( { :oauth_verifier => params[:pin] } )
180 m.reply "Error: There was a problem registering your Twitter account. Please make sure you have the right PIN. If the problem persists, use twitter authorize again to get a new PIN"
183 @registry[m.sourcenick + "_access_token"] = YAML::dump(@access_token)
184 m.reply "Okay, you're all set"
187 # update the status on twitter
188 def update_status(m, params)
189 unless @registry.has_key?(m.sourcenick + "_access_token")
190 m.reply "You must first authorize your Twitter account before tweeting."
193 @access_token = YAML::load(@registry[m.sourcenick + "_access_token"])
195 uri = "https://api.twitter.com/statuses/update.json"
196 msg = params[:status].to_s
199 m.reply "your status message update is too long, please keep it under 140 characters"
203 response = @access_token.post(uri, { :status => msg })
206 reply_method = params[:notify] ? :notify : :reply
207 if response.class == Net::HTTPOK
208 m.__send__(reply_method, "status updated")
210 m.__send__(reply_method, "could not update status")
214 # ties a nickname to a twitter username and password
215 def identify(m, params)
216 @registry[m.sourcenick + "_username"] = params[:username].to_s
217 @registry[m.sourcenick + "_password"] = params[:password].to_s
218 m.reply "you're all set up!"
221 # update on ACTION if the user has enabled the option
223 return unless m.action?
224 return unless @registry[m.sourcenick + "_actions"]
225 update_status(m, :status => m.message, :notify => true)
228 # show or toggle action twitting
229 def actions(m, params)
232 @registry[m.sourcenick + "_actions"] = true
235 @registry.delete(m.sourcenick + "_actions")
238 if @registry[m.sourcenick + "_actions"]
239 m.reply _("actions will be twitted")
241 m.reply _("actions will not be twitted")
247 # create an instance of our plugin class and register for the "length" command
248 plugin = TwitterPlugin.new
249 plugin.map 'twitter update *status', :action => "update_status", :threaded => true
250 plugin.map 'twitter authorize', :action => "authorize", :public => false
251 plugin.map 'twitter deauthorize', :action => "deauthorize", :public => false
252 plugin.map 'twitter pin :pin', :action => "pin", :public => false
253 plugin.map 'twitter actions [:toggle]', :action => "actions", :requirements => { :toggle => /^on|off$/ }
254 plugin.map 'twitter status [:nick]', :action => "get_status", :threaded => true
255 plugin.map 'twitter :friends [status] [:nick]', :action => "get_status", :requirements => { :friends => /^friends?$/ }, :threaded => true