4 # :title: Twitter Status Update for rbot
6 # Author:: Carter Parks (carterparks) <carter@carterparks.com>
7 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
9 # Copyright:: (C) 2007 Carter Parks
10 # Copyright:: (C) 2007 Giuseppe Bilotta
12 # Users can setup their twitter username and password and then begin updating
18 class TwitterPlugin < Plugin
19 Config.register Config::IntegerValue.new('twitter.status_count',
20 :default => 1, :validate => Proc.new { |v| v > 0 && v <= 10},
21 :desc => "Maximum number of status updates shown by 'twitter status'")
22 Config.register Config::IntegerValue.new('twitter.friends_status_count',
23 :default => 3, :validate => Proc.new { |v| v > 0 && v <= 10},
24 :desc => "Maximum number of status updates shown by 'twitter friends status'")
39 'X-Twitter-Client' => 'rbot twitter plugin'
43 # return a help string when the bot is asked for help on this plugin
44 def help(plugin, topic="")
45 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 identify [username] [password] => ties your nick to your twitter username and password | twitter actions [on|off] => enable/disable twitting of actions (/me does ...)"
48 # update the status on twitter
49 def get_status(m, params)
51 nick = params[:nick] || @registry[m.sourcenick + "_username"]
53 friends = params[:friends]
56 m.reply "you should specify the username of the twitter touse, or identify using 'twitter identify [username] [password]'"
60 user = URI.escape(nick)
62 count = @bot.config['twitter.status_count']
64 uri = "http://twitter.com/statuses/user_timeline/#{user}.xml?count=#{count}"
66 count = @bot.config['twitter.friends_status_count']
69 auth << URI.escape(@registry[m.sourcenick + "_username"])
71 auth << URI.escape(@registry[m.sourcenick + "_password"])
74 uri = "http://#{auth}twitter.com/statuses/friends_timeline/#{user}.xml"
77 response = @bot.httputil.get(uri, :headers => @header, :cache => false)
84 rex = REXML::Document.new(response)
85 rex.root.elements.each("status") { |st|
86 # month, day, hour, min, sec, year = st.elements['created_at'].text.match(/\w+ (\w+) (\d+) (\d+):(\d+):(\d+) \S+ (\d+)/)[1..6]
87 # debug [year, month, day, hour, min, sec].inspect
88 # time = Time.local(year.to_i, month, day.to_i, hour.to_i, min.to_i, sec.to_i)
89 time = Time.parse(st.elements['created_at'].text)
91 # Sometimes, time can be in the future; invert the relation in this case
92 delta = ((time > now) ? time - now : now - time)
93 msg = st.elements['text'].to_s + " (#{Utils.secs_to_string(delta.to_i)} ago via #{st.elements['source'].to_s})"
96 author = Utils.decode_html_entities(st.elements['user'].elements['name'].text) + ": " rescue ""
98 texts << author+Utils.decode_html_entities(msg).ircify_html
101 # friends always return the latest 20 updates, so we clip the count
107 m.reply "could not parse status for #{nick}'s friends"
109 m.reply "could not parse status for #{nick}"
113 m.reply texts.reverse.join("\n")
117 rep = "could not get status for #{nick}'s friends"
118 rep << ", try asking in private" unless m.private?
120 rep = "could not get status for #{nick}"
127 # update the status on twitter
128 def update_status(m, params)
131 unless @registry.has_key?(m.sourcenick + "_password") && @registry.has_key?(m.sourcenick + "_username")
132 m.reply "you must identify using 'twitter identify [username] [password]'"
136 user = URI.escape(@registry[m.sourcenick + "_username"])
137 pass = URI.escape(@registry[m.sourcenick + "_password"])
138 uri = "http://#{user}:#{pass}@twitter.com/statuses/update.xml"
140 msg = params[:status].to_s
143 m.reply "your status message update is too long, please keep it under 140 characters if possible, 160 characters maximum"
148 m.reply "your status message is longer than 140 characters, which is not optimal, but I'm going to update anyway"
151 source = "source=rbot"
152 msg = "status=#{CGI.escape(msg)}"
153 body = [source,msg].join("&")
155 response = @bot.httputil.post(uri, body, :headers => @header)
158 reply_method = params[:notify] ? :notify : :reply
159 if response.class == Net::HTTPOK
160 m.__send__(reply_method, "status updated")
162 m.__send__(reply_method, "could not update status")
166 # ties a nickname to a twitter username and password
167 def identify(m, params)
168 @registry[m.sourcenick + "_username"] = params[:username].to_s
169 @registry[m.sourcenick + "_password"] = params[:password].to_s
170 m.reply "you're all setup!"
173 # update on ACTION if the user has enabled the option
175 return unless m.action?
176 return unless @registry[m.sourcenick + "_actions"]
177 update_status(m, :status => m.message, :notify => true)
180 # show or toggle action twitting
181 def actions(m, params)
184 @registry[m.sourcenick + "_actions"] = true
187 @registry.delete(m.sourcenick + "_actions")
190 if @registry[m.sourcenick + "_actions"]
191 m.reply _("actions will be twitted")
193 m.reply _("actions will not be twitted")
199 # create an instance of our plugin class and register for the "length" command
200 plugin = TwitterPlugin.new
201 plugin.map 'twitter identify :username :password', :action => "identify", :public => false
202 plugin.map 'twitter update *status', :action => "update_status", :threaded => true
203 plugin.map 'twitter status [:nick]', :action => "get_status", :threaded => true
204 plugin.map 'twitter actions [:toggle]', :action => "actions", :requirements => { :toggle => /^on|off$/ }
205 plugin.map 'twitter :friends [status] [:nick]', :action => "get_status", :requirements => { :friends => /^friends?$/ }, :threaded => true