commit 205a27cc952d930075469215bf4228e61d4d0b35
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Tue Mar 15 01:10:42 2011 +0100

    [evol] preliminary Messenger cyborg

diff --git a/bin/messenger b/bin/messenger
new file mode 100755
index 0000000..1e2cb9a
--- /dev/null
+++ b/bin/messenger
@@ -0,0 +1,102 @@
+#!/usr/bin/ruby -Ku
+
+#--
+# CyborgHood, a distributed system management software.
+# Copyright (c) 2009-2011 Marc Dequènes (Duck) <Duck@DuckCorp.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#++
+
+# to allow in-place run for test
+$: << File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'cyborghood'
+
+
+module CyborgHood
+  module MessengerLand
+    class Messenger < Cyborg
+      # load config before modules are included
+      Config.load(self.human_name)
+
+      inject_deferred_translations
+
+      include BotNet
+
+      attr_reader :user
+
+      def setup
+        super
+
+        # TODO: find user, do not trust ENV (use sudo trusted env?)
+        @user = ENV['USER']
+
+        define_interface "0.1~"
+
+        @terminal_cli = self.services.terminal_cli
+        @terminal_cli.when_command_received do |cmd, refs, &cb|
+          task 'execute_command' do
+            # TODO: handle syms into cmd (useful when refs are used)
+            clerk_cmd = (['', 'Commands'] + cmd).join('/')
+            set_user bot.user
+            ask "Clerk", :cmd_result, clerk_cmd, refs
+
+            on_error do
+              cb.call false
+            end
+            on_success do
+              cb.call true, results[:cmd_result]
+            end
+          end
+        end
+        @terminal_cli.when_finished do
+          stop :quickly
+        end
+
+        # TODO: handle middleware_cli, if choosen middleware support it
+      end
+
+      def start_work
+        @terminal_cli.run
+      end
+
+      def stop(condition)
+        super do
+          @terminal_cli.stop
+        end
+      end
+    end
+  end # MessengerLand
+end
+
+
+reg = Needle::Registry.new
+reg.define do |b|
+  b.require 'cyborghood', CyborgHood
+  b.require 'cyborghood-messenger/land', CyborgHood::MessengerLand
+
+  b.bot { CyborgHood::MessengerLand::Messenger.new(b.messenger_land) }
+end
+
+bot = reg.bot
+
+
+trap('INT') do
+  bot.stop(:quickly)
+end
+trap('TERM') do
+  bot.stop(:quickly)
+end
+
+bot.run
diff --git a/lib/cyborghood-messenger/land.rb b/lib/cyborghood-messenger/land.rb
new file mode 100644
index 0000000..61565fe
--- /dev/null
+++ b/lib/cyborghood-messenger/land.rb
@@ -0,0 +1,35 @@
+#--
+# CyborgHood, a distributed system management software.
+# Copyright (c) 2009-2011 Marc Dequènes (Duck) <Duck@DuckCorp.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#++
+
+
+module CyborgHood
+  module MessengerLand
+    add_deferred_translations 'messenger'
+
+    def register_services(container)
+      container.namespace_define(:messenger_land) do |b|
+        b.terminal_cli do
+          require 'cyborghood-messenger/terminal_cli'
+          TerminalCLI.new
+        end
+      end
+    end
+
+    module_function :register_services
+  end
+end
diff --git a/lib/cyborghood-messenger/terminal_cli.rb b/lib/cyborghood-messenger/terminal_cli.rb
new file mode 100644
index 0000000..dd40142
--- /dev/null
+++ b/lib/cyborghood-messenger/terminal_cli.rb
@@ -0,0 +1,107 @@
+#--
+# CyborgHood, a distributed system management software.
+# Copyright (c) 2009-2011 Marc Dequènes (Duck) <Duck@DuckCorp.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#++
+
+require 'readline'
+require 'shellwords'
+
+
+module CyborgHood
+  module MessengerLand
+    class TerminalCLI
+      add_deferred_translations 'messenger'
+
+      def initialize
+        # save state of the terminal
+        @stty_save = `stty -g`.chomp
+
+        @thread = nil
+      end
+
+      def when_finished(&cb)
+        @finished_cb = cb
+      end
+
+      def when_command_received(&cb)
+        @cmd_recv_cb = cb
+      end
+
+      def run
+        if @thread
+          @thread.wakeup
+          return
+        end
+
+        @thread = Thread.new do
+          # TODO: handle completion
+          while line = Readline.readline('> ', true)
+            # remove empty lines, comments, or dups from history
+            if line =~ /^\s*(#.*)?$/ or Readline::HISTORY.to_a[-2] == line
+              Readline::HISTORY.pop
+              next
+            end
+
+            begin
+              raw_cmd_parts = line.shellsplit
+            rescue
+              STDERR.puts "Syntax error in command."
+              next
+            end
+
+            # TODO: handle files arguments (sym as ref)
+            cmd = raw_cmd_parts
+            refs = {}
+
+            if @cmd_recv_cb
+              EM.schedule do
+                @cmd_recv_cb.call(cmd, refs) do |*reply|
+                  ok, messages = reply
+
+                  if ok
+                    messages.each{|msg| puts msg }
+                  else
+                    puts _("Internal error")
+                  end
+
+                  self.run
+                end
+              end
+            end
+            Thread.stop
+          end
+
+          # finished
+          puts # newline to skip leftover prompt
+          logger.debug "CLI: user exited"
+          EM.schedule(@finished_cb) if @finished_cb
+        end
+      end
+
+      def pause
+        @thread.stop
+      end
+
+      def stop
+        @thread.kill
+        @thread = nil
+
+        # restore state of the terminal
+        system('stty', @stty_save)
+      end
+    end
+  end # MessengerLand
+end
