Jabber Bot
changeset 0:26986fceeb03 tip
Added John's Code
| author | root@riviera.nuigalway.ie |
|---|---|
| date | Fri Jun 27 22:01:59 2008 +0100 (2008-06-27) |
| parents | |
| children | |
| files | .DS_Store bot.rb lib/timer.rb rjbotGeneric.rb |
line diff
1.1 Binary file .DS_Store has changed
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/bot.rb Fri Jun 27 22:01:59 2008 +0100 2.3 @@ -0,0 +1,62 @@ 2.4 +#/usr/bin/env ruby 2.5 +require 'rubygems' 2.6 +require 'jabber/bot' 2.7 + 2.8 +# 2.9 +# Set the lib path 2.10 +# 2.11 +defaultlib = File.expand_path(File.dirname($0) + '/lib') 2.12 + 2.13 +if File.directory? "#{defaultlib}" 2.14 + unless $:.include? defaultlib 2.15 + $:.unshift defaultlib 2.16 + end 2.17 +end 2.18 + 2.19 + 2.20 +require 'timer' 2.21 + 2.22 +# Create a public Jabber::Bot 2.23 +bot = Jabber::Bot.new( 2.24 + :jabber_id => '', # The bot's jabber ID 2.25 + :password => '', # The bot's jabber ID Password 2.26 + :master => '', # The owner of the bot 2.27 + :is_public => true 2.28 +) 2.29 + 2.30 +# Give your bot a public command 2.31 +bot.add_command( 2.32 + :syntax => 'rand', 2.33 + :description => 'Produce a random number from 0 to 10', 2.34 + :regex => /^rand$/, 2.35 + :is_public => true 2.36 +) { rand(10).to_s } 2.37 + 2.38 +# Give your bot a private command with an alias 2.39 +bot.add_command( 2.40 + :syntax => 'puts <string>', 2.41 + :description => 'Write something to $stdout', 2.42 + :regex => /^puts\s+.+$/, 2.43 + :alias => [ 2.44 + :syntax => 'p <string>', 2.45 + :regex => /^p\s+.+$/ 2.46 + ] 2.47 +) do |sender, message| 2.48 + puts message 2.49 + "'#{message}' written to $stdout" 2.50 +end 2.51 + 2.52 +# Reminder 2.53 +bot.add_command( 2.54 + :syntax => 'remind <string>', 2.55 + :description => 'Remind me about something in 10seconds', 2.56 + :regex => /^remind\s+.+$/ 2.57 +) do |sender, message| 2.58 + @timer = Timer.new 2.59 + @timer.add_once(10) { bot.deliver(sender, message) } 2.60 + 2.61 + "You will be reminded in 10seconds" 2.62 +end 2.63 + 2.64 +# Bring your new bot to life 2.65 +bot.connect
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/lib/timer.rb Fri Jun 27 22:01:59 2008 +0100 3.3 @@ -0,0 +1,270 @@ 3.4 +# changes: 3.5 +# 1. Timer::Timer ---> Timer 3.6 +# 2. timer id is now the object_id of the action 3.7 +# 3. Timer resolution removed, we're always arbitrary precision now 3.8 +# 4. I don't see any obvious races [not that i did see any in old impl, though] 3.9 +# 5. We're tickless now, so no need to jerk start/stop 3.10 +# 6. We should be pretty fast now, wrt old impl 3.11 +# 7. reschedule/remove/block now accept nil as an action id (meaning "current") 3.12 +# 8. repeatability is ignored for 0-period repeatable timers 3.13 +# 9. configure() method superceeds reschedule() [the latter stays as compat] 3.14 + 3.15 +require 'thread' 3.16 +require 'monitor' 3.17 + 3.18 +# Timer handler, manage multiple Action objects, calling them when required. 3.19 +# When the Timer is constructed, a new Thread is created to manage timed 3.20 +# delays and run Actions. 3.21 +# 3.22 +# XXX: there is no way to stop the timer currently. I'm keeping it this way 3.23 +# to weed out old Timer implementation legacy in rbot code. -jsn. 3.24 +class Timer 3.25 + 3.26 + # class representing individual timed action 3.27 + class Action 3.28 + 3.29 + # Time when the Action should be called next 3.30 + attr_accessor :next 3.31 + 3.32 + # Options are: 3.33 + # start:: Time when the Action should be run for the first time. 3.34 + # Repeatable Actions will be repeated after that, see 3.35 + # :period. One-time Actions will not (obviously) 3.36 + # Default: Time.now + :period 3.37 + # period:: How often repeatable Action should be run, in seconds. 3.38 + # Default: 1 3.39 + # blocked:: if true, Action starts as blocked (i.e. will stay dormant 3.40 + # until unblocked) 3.41 + # args:: Arguments to pass to the Action callback. Default: [] 3.42 + # repeat:: Should the Action be called repeatedly? Default: false 3.43 + # code:: You can specify the Action body using &block, *or* using 3.44 + # this option. 3.45 + 3.46 + def initialize(options = {}, &block) 3.47 + opts = { 3.48 + :period => 1, 3.49 + :blocked => false, 3.50 + :args => [], 3.51 + :repeat => false 3.52 + }.merge(options) 3.53 + 3.54 + @block = nil 3.55 + # debug("adding timer #{self} :period => #{opts[:period]}, :repeat => #{opts[:repeat].inspect}") 3.56 + self.configure(opts, &block) 3.57 + # debug("added #{self}") 3.58 + end 3.59 + 3.60 + # Provides for on-the-fly reconfiguration of the Actions 3.61 + # Accept the same arguments as the constructor 3.62 + def configure(opts = {}, &block) 3.63 + @period = opts[:period] if opts.include? :period 3.64 + @blocked = opts[:blocked] if opts.include? :blocked 3.65 + @repeat = opts[:repeat] if opts.include? :repeat 3.66 + 3.67 + if block_given? 3.68 + @block = block 3.69 + elsif opts[:code] 3.70 + @block = opts[:code] 3.71 + end 3.72 + 3.73 + raise 'huh?? blockless action?' unless @block 3.74 + if opts.include? :args 3.75 + @args = Array === opts[:args] ? opts[:args] : [opts[:args]] 3.76 + end 3.77 + 3.78 + if opts[:start] and (Time === opts[:start]) 3.79 + self.next = opts[:start] 3.80 + else 3.81 + self.next = Time.now + (opts[:start] || @period) 3.82 + end 3.83 + end 3.84 + 3.85 + # modify the Action period 3.86 + def reschedule(period, &block) 3.87 + self.configure(:period => period, &block) 3.88 + end 3.89 + 3.90 + # blocks an Action, so it won't be run 3.91 + def block 3.92 + @blocked = true 3.93 + end 3.94 + 3.95 + # unblocks a blocked Action 3.96 + def unblock 3.97 + @blocked = false 3.98 + end 3.99 + 3.100 + def blocked? 3.101 + @blocked 3.102 + end 3.103 + 3.104 + # calls the Action callback, resets .next to the Time of the next call, 3.105 + # if the Action is repeatable. 3.106 + def run(now = Time.now) 3.107 + raise 'inappropriate time to run()' unless self.next && self.next <= now 3.108 + self.next = nil 3.109 + begin 3.110 + @block.call(*@args) 3.111 + rescue Exception => e 3.112 + error "Timer action #{self.inspect}: block #{@block.inspect} failed!" 3.113 + error e.pretty_inspect 3.114 + # debug e.backtrace.join("\n") 3.115 + end 3.116 + 3.117 + if @repeat && @period > 0 3.118 + self.next = now + @period 3.119 + end 3.120 + 3.121 + return self.next 3.122 + end 3.123 + end 3.124 + 3.125 + # creates a new Timer and starts it. 3.126 + def initialize 3.127 + self.extend(MonitorMixin) 3.128 + @tick = self.new_cond 3.129 + @thread = nil 3.130 + @actions = Hash.new 3.131 + @current = nil 3.132 + self.start 3.133 + end 3.134 + 3.135 + # Creates and installs a new Action, repeatable by default. 3.136 + # _period_:: Action period 3.137 + # _opts_:: options for Action#new, see there 3.138 + # _block_:: Action callback code 3.139 + # 3.140 + # Returns the id of the created Action 3.141 + def add(period, opts = {}, &block) 3.142 + a = Action.new({:repeat => true, :period => period}.merge(opts), &block) 3.143 + self.synchronize do 3.144 + @actions[a.object_id] = a 3.145 + @tick.signal 3.146 + end 3.147 + return a.object_id 3.148 + end 3.149 + 3.150 + # Creates and installs a new Action, one-time by default. 3.151 + # _period_:: Action delay 3.152 + # _opts_:: options for Action#new, see there 3.153 + # _block_:: Action callback code 3.154 + # 3.155 + # Returns the id of the created Action 3.156 + def add_once(period, opts = {}, &block) 3.157 + self.add(period, {:repeat => false}.merge(opts), &block) 3.158 + end 3.159 + 3.160 + # blocks an existing Action 3.161 + # _aid_:: Action id, obtained previously from add() or add_once() 3.162 + def block(aid) 3.163 + # debug "blocking #{aid}" 3.164 + self.synchronize { self[aid].block } 3.165 + end 3.166 + 3.167 + # unblocks an existing blocked Action 3.168 + # _aid_:: Action id, obtained previously from add() or add_once() 3.169 + def unblock(aid) 3.170 + # debug "unblocking #{aid}" 3.171 + self.synchronize do 3.172 + self[aid].unblock 3.173 + @tick.signal 3.174 + end 3.175 + end 3.176 + 3.177 + # removes an existing blocked Action 3.178 + # _aid_:: Action id, obtained previously from add() or add_once() 3.179 + def remove(aid) 3.180 + self.synchronize do 3.181 + @actions.delete(aid) # or raise "nonexistent action #{aid}" 3.182 + end 3.183 + end 3.184 + 3.185 + alias :delete :remove 3.186 + 3.187 + # Provides for on-the-fly reconfiguration of Actions 3.188 + # _aid_:: Action id, obtained previously from add() or add_once() 3.189 + # _opts_:: see Action#new 3.190 + # _block_:: (optional) new Action callback code 3.191 + def configure(aid, opts = {}, &block) 3.192 + self.synchronize do 3.193 + self[aid].configure(opts, &block) 3.194 + @tick.signal 3.195 + end 3.196 + end 3.197 + 3.198 + # changes Action period 3.199 + # _aid_:: Action id 3.200 + # _period_:: new period 3.201 + # _block_:: (optional) new Action callback code 3.202 + def reschedule(aid, period, &block) 3.203 + self.configure(aid, :period => period, &block) 3.204 + end 3.205 + 3.206 + def start 3.207 + raise 'already started' if @thread 3.208 + @stopping = false 3.209 + # debug "starting timer #{self}" 3.210 + @thread = Thread.new do 3.211 + loop do 3.212 + tmout = self.run_actions 3.213 + break if tmout and tmout < 0 3.214 + self.synchronize { @tick.wait(tmout) } 3.215 + end 3.216 + end 3.217 + end 3.218 + 3.219 + def stop 3.220 + raise 'already stopped' unless @thread 3.221 + # debug "stopping timer #{self}..." 3.222 + @stopping = true 3.223 + self.synchronize { @tick.signal } 3.224 + @thread.join(60) or @thread.kill 3.225 + # debug "timer #{self} stopped" 3.226 + @thread = nil 3.227 + end 3.228 + 3.229 + protected 3.230 + 3.231 + def [](aid) 3.232 + aid ||= @current 3.233 + raise "no current action" unless aid 3.234 + raise "nonexistent action #{aid}" unless @actions.include? aid 3.235 + @actions[aid] 3.236 + end 3.237 + 3.238 + def run_actions(now = Time.now) 3.239 + nxt = nil 3.240 + @actions.keys.each do |k| 3.241 + return -1 if @stopping 3.242 + a = @actions[k] 3.243 + next if (!a) or a.blocked? 3.244 + 3.245 + if a.next <= now 3.246 + begin 3.247 + @current = k 3.248 + v = a.run(now) 3.249 + ensure 3.250 + @current = nil 3.251 + end 3.252 + 3.253 + unless v 3.254 + @actions.delete k 3.255 + next 3.256 + end 3.257 + else 3.258 + v = a.next 3.259 + end 3.260 + 3.261 + nxt = v if v and ((!nxt) or (v < nxt)) 3.262 + end 3.263 + 3.264 + if nxt 3.265 + delta = nxt - now 3.266 + delta = 0 if delta < 0 3.267 + return delta 3.268 + else 3.269 + return nil 3.270 + end 3.271 + end 3.272 + 3.273 +end
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/rjbotGeneric.rb Fri Jun 27 22:01:59 2008 +0100 4.3 @@ -0,0 +1,62 @@ 4.4 +#/usr/bin/env ruby 4.5 +require 'rubygems' 4.6 +require 'jabber/bot' 4.7 + 4.8 +# 4.9 +# Set the lib path 4.10 +# 4.11 +defaultlib = File.expand_path(File.dirname($0) + '/lib') 4.12 + 4.13 +if File.directory? "#{defaultlib}" 4.14 + unless $:.include? defaultlib 4.15 + $:.unshift defaultlib 4.16 + end 4.17 +end 4.18 + 4.19 + 4.20 +require 'timer' 4.21 + 4.22 +# Create a public Jabber::Bot 4.23 +bot = Jabber::Bot.new( 4.24 + :jabber_id => 'johnlong14@gmail.com', # The bot's jabber ID 4.25 + :password => 'xxxxxx', # The bot's jabber ID Password 4.26 + :master => 'johnlong@jabber.org', # The owner of the bot 4.27 + :is_public => true 4.28 +) 4.29 + 4.30 +# Give your bot a public command 4.31 +bot.add_command( 4.32 + :syntax => 'rand', 4.33 + :description => 'Produce a random number from 0 to 10', 4.34 + :regex => /^rand$/, 4.35 + :is_public => true 4.36 +) { rand(10).to_s } 4.37 + 4.38 +# Give your bot a private command with an alias 4.39 +bot.add_command( 4.40 + :syntax => 'puts <string>', 4.41 + :description => 'Write something to $stdout', 4.42 + :regex => /^puts\s+.+$/, 4.43 + :alias => [ 4.44 + :syntax => 'p <string>', 4.45 + :regex => /^p\s+.+$/ 4.46 + ] 4.47 +) do |sender, message| 4.48 + puts message 4.49 + "'#{message}' written to $stdout" 4.50 +end 4.51 + 4.52 +# Reminder 4.53 +bot.add_command( 4.54 + :syntax => 'remind <string>', 4.55 + :description => 'Remind me about something in 10seconds', 4.56 + :regex => /^remind\s+.+$/ 4.57 +) do |sender, message| 4.58 + @timer = Timer.new 4.59 + @timer.add_once(10) { bot.deliver(sender, message) } 4.60 + 4.61 + "You will be reminded in 10seconds" 4.62 +end 4.63 + 4.64 +# Bring your new bot to life 4.65 +bot.connect
