Revision 612572f9
Added by Marc Dequènes about 14 years ago
- ID 612572f9c30120c90f48b9ba3ddd71288c258d2a
lib/cyborghood/cyborg/dsl.rb | ||
---|---|---|
self.instance_eval(&block)
|
||
_start_dsl
|
||
end
|
||
|
||
reveal logger
|
||
end
|
||
|
||
class Task < BaseDSL
|
||
... | ... | |
@error_cb = nil
|
||
@success_cb = nil
|
||
|
||
@logguer = Logger.instance
|
||
@notification_name = "task/#{@name}"
|
||
|
||
begin
|
||
super(&block)
|
||
rescue
|
||
@logger.error "Task '#{@name}': could not initialize DSL: " + $!.to_s
|
||
@logger.debug $!.backtrace.join("\n")
|
||
logger.error "Task '#{@name}': could not initialize DSL: " + $!.to_s
|
||
logger.debug $!.backtrace.join("\n")
|
||
@cancel_run = true
|
||
end
|
||
end
|
||
|
||
# may return a Hash of results
|
||
def schedule(&job)
|
||
_add_subtask("job/#{job.hash}") do |subtask_cb|
|
||
@logger.debug "Task '#{@name}': Scheduling job"
|
||
_add_subtask("job/#{job.hash}") do |subtask|
|
||
logger.debug "Task '#{@name}': Scheduling job"
|
||
cb = Proc.new do
|
||
subtask_cb true
|
||
subtask.finish
|
||
end
|
||
@bot.schedule_task(cb, job)
|
||
end
|
||
... | ... | |
def send_notification(name, data)
|
||
name = @notification_name if name == :thread
|
||
|
||
_add_subtask("notification/#{name}/out") do |subtask_cb|
|
||
@logger.debug "Task '#{@name}': Sending notification to '#{name}'"
|
||
_add_subtask("notification/#{name}/out") do |subtask|
|
||
logger.debug "Task '#{@name}': Sending notification to '#{name}'"
|
||
chan = @bot.get_channel(name)
|
||
chan << data
|
||
subtask_cb.call(true)
|
||
subtask.finish
|
||
end
|
||
end
|
||
|
||
... | ... | |
|
||
full_results = {}
|
||
|
||
_add_subtask("notification/#{name}/in") do |subtask_cb|
|
||
@logger.debug "Task '#{@name}': Waiting notification on '#{name}'"
|
||
_add_subtask("notification/#{name}/in") do |subtask|
|
||
logger.debug "Task '#{@name}': Waiting notification on '#{name}'"
|
||
subcription_id = @bot.get_channel(name).subscribe do |msg|
|
||
if _notification_criterias_match(msg, criterias)
|
||
stop, results = cb.call(msg)
|
||
@bot.get_channel(name).unsubscribe(subcription_id) if stop
|
||
subtask_cb.call(stop, results)
|
||
cb.call(subtask, msg)
|
||
@bot.get_channel(name).unsubscribe(subcription_id) if subtask.finished?
|
||
end
|
||
end
|
||
|
||
if timeout
|
||
EventMachine.add_timer(timeout) do
|
||
@bot.get_channel(name).unsubscribe(subcription_id)
|
||
subtask_cb.call
|
||
subtask.finish
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
def wait_timer(timeout, repeat = false, &cb)
|
||
_add_subtask("timer/#{timeout}/#{cb.hash}") do |subtask_cb|
|
||
_add_subtask("timer/#{timeout}/#{cb.hash}") do |subtask|
|
||
periodic_timer_signature = nil
|
||
|
||
timer_cb = Proc.new do
|
||
if repeat
|
||
stop, results = cb.call
|
||
EventMachine.cancel_timer(periodic_timer_signature) if stop
|
||
cb.call(subtask)
|
||
EventMachine.cancel_timer(periodic_timer_signature) if subtask.finished?
|
||
else
|
||
stop = true
|
||
results = cb.call
|
||
cb.call(subtask)
|
||
subtask.finish
|
||
end
|
||
subtask_cb.call(stop, results)
|
||
end
|
||
|
||
if repeat
|
||
... | ... | |
end
|
||
|
||
def task(name = nil, &block)
|
||
_add_subtask("task/#{name}") do |subtask_cb|
|
||
_add_subtask("task/#{name}") do |subtask|
|
||
self.class.new(name || @name, &block)
|
||
subtask_cb.call(true)
|
||
subtask.finish
|
||
end
|
||
end
|
||
|
||
... | ... | |
end
|
||
|
||
def stop_bot(condition)
|
||
_add_subtask('stop') do |subtask_cb|
|
||
_add_subtask('stop') do |subtask|
|
||
case condition
|
||
when :when_finished
|
||
# try to stop gracefully
|
||
... | ... | |
@bot.stop
|
||
end
|
||
|
||
subtask_cb.call(true)
|
||
subtask.finish
|
||
end
|
||
end
|
||
|
||
protected
|
||
|
||
def _add_subtask(name, cb = nil, &block)
|
||
# TODO: create a Subtask object, with the following attributes, plus the subtask_cb
|
||
# and pass it to the block instead of the subtask_cb, to allow easier
|
||
# manipulation of errors/results/... and being more extensible
|
||
subtask = {
|
||
:name => name,
|
||
:done => false,
|
||
:results => {},
|
||
:errors => []
|
||
}
|
||
class Subtask
|
||
attr_reader :name
|
||
attr_accessor :results, :errors
|
||
|
||
# can be called multiple times with stop=false to collect result
|
||
# and stop=true will consider it stopper
|
||
subtask_cb = Proc.new do |stop, results|
|
||
if subtask[:done]
|
||
subtask[:error] = CyberError.new(:unrecoverable, "botnet/client/dsl", "Task '#{@name}': subtask should have ended, but it lied")
|
||
else
|
||
if results.is_a? CyberError
|
||
subtask[:errors] << results
|
||
elsif results.is_a? Hash
|
||
subtask[:results].merge results
|
||
elsif not results.nil? # nil just mean no result
|
||
subtask[:errors] << CyberError.new(:unrecoverable, "botnet/client/dsl", "Task '#{@name}': unknown result for subtask")
|
||
end
|
||
def initialize(name, &block)
|
||
@name = name
|
||
@block = block
|
||
|
||
if stop
|
||
subtask[:done] = true
|
||
_check_finished
|
||
end
|
||
end
|
||
@finished = false
|
||
@results = {}
|
||
@errors = []
|
||
end
|
||
|
||
subtask[:block] = Proc.new do
|
||
begin
|
||
block.call subtask_cb
|
||
rescue
|
||
msg = "Task '#{@name}': error: " + $!.to_s
|
||
logger.error msg
|
||
subtask[:errors] << CyberError.new(:unrecoverable, "botnet/client/dsl", msg)
|
||
subtask[:done] = true
|
||
def do_it
|
||
@block.call self
|
||
rescue
|
||
msg = "Task '#{@name}': error: " + $!.to_s
|
||
logger.error msg
|
||
@errors << CyberError.new(:unrecoverable, "botnet/client/dsl", msg)
|
||
finish
|
||
end
|
||
|
||
def finish
|
||
if @finished
|
||
raise CyberError.new(:unrecoverable, "botnet/client/dsl", "Task '#{@name}': subtask should have ended, but it lied")
|
||
end
|
||
@finished = true
|
||
end
|
||
|
||
@subtasks << subtask
|
||
def finished?
|
||
@finished
|
||
end
|
||
end
|
||
|
||
nil
|
||
def _add_subtask(name, cb = nil, &block)
|
||
@subtasks << Subtask.new(name, &block)
|
||
end
|
||
|
||
def _subtasks_finished?
|
||
@subtasks.each do |subtask|
|
||
return false unless subtask[:done]
|
||
return false unless subtask.finished?
|
||
end
|
||
return true
|
||
end
|
||
... | ... | |
|
||
@subtasks.each do |subtask|
|
||
# skip broken or canceled subtasks
|
||
next if subtask[:done]
|
||
subtask[:block].call
|
||
next if subtask.finished?
|
||
subtask.do_it
|
||
end
|
||
|
||
true
|
Also available in: Unified diff
[evol] Task-based client DSL §2 (Subtask and logger)