<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>usefulfor.com/ruby &#187; Shell Script</title>
	<atom:link href="http://usefulfor.com/ruby/category/shell-script/feed/" rel="self" type="application/rss+xml" />
	<link>http://usefulfor.com/ruby</link>
	<description>ruby goodness for your daily needs</description>
	<lastBuildDate>Mon, 07 Dec 2009 23:20:56 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>rComic: comic strip downloader</title>
		<link>http://usefulfor.com/ruby/2007/10/23/rcomic-comic-strip-downloader/</link>
		<comments>http://usefulfor.com/ruby/2007/10/23/rcomic-comic-strip-downloader/#comments</comments>
		<pubDate>Tue, 23 Oct 2007 12:26:56 +0000</pubDate>
		<dc:creator>etd</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Shell Script]]></category>

		<guid isPermaLink="false">http://weblog.nomejortu.com/?p=28</guid>
		<description><![CDATA[rComic is a small script to download and display Internet comic strips. To add new strips, you only need to modify the config file. And it is an interesting exercise to play with the Net::HTTP and YAML libraries.

rComic makes use of two external programs: wget and display for downloading and displaying the image. Both tools [...]]]></description>
			<content:encoded><![CDATA[<p>rComic is a small script to download and display Internet comic strips. To add new strips, you only need to modify the config file. And it is an interesting exercise to play with the <a href="http://www.ruby-doc.org/core/classes/Net/HTTP.html">Net::HTTP</a> and <a href="http://www.ruby-doc.org/core/classes/YAML.html">YAML</a> libraries.<br />
<span id="more-31"></span><br />
rComic makes use of two external programs: <code>wget</code> and <code>display</code> for downloading and displaying the image. Both tools are available as packages in all major distros (look for <code>imagemagick</code>). Get <a href="/ruby/files/2008/06/rcomic.tar.gz">the code</a> and let&#8217;s get it started.</p>
<p>To store the information of our comic strips we will be using <acronym title="YAML Ain'tMarkup Language">YAML</acronym>:</p>
<blockquote><p>The YAML library serializes and deserializes Ruby object trees to and from and external, readable, plain-text format.</p></blockquote>
<p>In the config file (<code>rcomic.yaml</code>) every comic strip definition will have the following appearance:</p>
<div class="hl-surround" ><div class="hl-main"><pre>xkcd:
  desc: A webcomic of romance, sarcasm, math, and language.
  host: www.xkcd.com
  path: /
  rexp: &lt;img&gt;</pre></div></div>
<p>You need a <strong>key word</strong> that will be used to refer to the strip and a set of configuration parameters, the host name, the path inside the server and a regular expression to identify the desired image.</p>
<p>In order to add a new strip, you only need to append a block like the one above to your configuration file.</p>
<p>Three steps are performed in the script: command line parsing, HTTP connection and download of the page, image download and display.  In order to know what strip are we working on, first we need to load the configuration file as show:-</p>
<div class="hl-surround" ><div class="hl-main"><pre>#load configuration
config = YAML.load_file('rcomic.yaml')</pre></div></div>
<p>Then some simple logic determines if the requested strip (the first argument provided) is configured in the <code>.yaml</code> file. If no reference to the <strong>key word</strong> is found  in the YAML file, a help message is displayed. Otherwise, we carry on with the next steps:</p>
<div class="hl-surround" ><div class="hl-main"><pre># prepare an HTTP connection
http = Net::HTTP.new($host)
# get the page
response = http.get($path)

# scan for the image
img = response.body.scan($rexp).first.first
file = File.basename(img)</pre></div></div>
<p>First we request the page and then we apply the regular expression to the body of the HTML returned. The <code>img</code> variable will contain the full URL to the image (i.e. <code>http://imgs.xkcd.com/comics/gyroscopes.png</code>) and the <code>file</code> will contain only the file name (i.e. <code>gyroscopes.png</code>).</p>
<p>With this information is dead easy to download and display the images:</p>
<div class="hl-surround" ><div class="hl-main"><pre># download (if not already downloaded)
unless File.exist?(&quot;/tmp/#{file}&quot;)
  `wget -O /tmp/#{file} #{img}`
end

# display
`display /tmp/#{file}`</pre></div></div>
<p>As a side note, we will only download the file if the file is not already present in our <code>/tmp/</code> folder.</p>
<p>Happy comics <img src='http://usefulfor.com/ruby/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<img src="http://usefulfor.com/ruby/?ak_action=api_record_view&id=31&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://usefulfor.com/ruby/2007/10/23/rcomic-comic-strip-downloader/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ruby bot: email processing</title>
		<link>http://usefulfor.com/ruby/2007/08/07/ruby-bot-email-processing/</link>
		<comments>http://usefulfor.com/ruby/2007/08/07/ruby-bot-email-processing/#comments</comments>
		<pubDate>Mon, 06 Aug 2007 22:11:56 +0000</pubDate>
		<dc:creator>etd</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Shell Script]]></category>

		<guid isPermaLink="false">http://weblog.nomejortu.com/?p=20</guid>
		<description><![CDATA[Pinky: Gee, Brain, what are we going to do tonight?
Brain: The same thing we do every night, try to take over the world!
Have you ever wanted to have the ability to send commands to your box using email? Use RubyBot, the brand new plugin-driven ruby script that makes the task of taking over the world [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p><strong>Pinky</strong>: Gee, Brain, what are we going to do tonight?<br />
<strong>Brain</strong>: The same thing we do every night, try to take over the world!</p></blockquote>
<p>Have you ever wanted to have the ability to send commands to your box using email? Use RubyBot, the brand new plugin-driven ruby script that makes the task of taking over the world a bit easier!<br />
<span id="more-23"></span><br />
Goals of the project:</p>
<ul>
<li>We want to have a script that can be used directly by the <acronym title="Mail Transfer Agent">MTA</acronym> (much in the way <a href="http://www.procmail.org/">procmail</a> works).</li>
<li>We want to deal with the internals of email format as less as possible.</li>
<li>Flexibility is an issue! We want to be able to do all sorts of things.</li>
<li>We want to have some feedback/output from our commands</li>
</ul>
<p>But first, stand and relax, there is lots of stuff comming in this post, so maybe it is a good idea to download <a href="/ruby/files/2008/06/rubybot.tar.bz2">the code</a> and have a look at it before we start. <img src='http://usefulfor.com/ruby/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><acronym title="Mail Transfer Agent">MTA</acronym>s can usually be configured to pass received messages to certain applications. The internals of how this mechanism works is out of the scope of this post. However, as an example, let&#8217;s see how <a href="http://www.qmail.org/">qmail</a> uses the <strong>.qmail</strong> files to do it. The following <strong>.qmail</strong> file will pipe the contents of all the incoming mail to our script:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>|./rubybot.rb</pre></div></div>
<p>The first thing thatour script needs to do is to get the piped message. In ruby we do this by reading the contents of the standar input:</p>
<div class="hl-surround" ><div class="hl-main"><pre>#0: get email from standard input
email = ''
while gets
  email &lt;&lt; $_
end</pre></div></div>
<p>The previous code will store in the <code>email</code> variable the contents of the email. Instead of trying to parse the email with regular expressions we are going to use the <a href="http://wiki.rubyonrails.org/rails/pages/ActionMailer">ActionMailer</a> package of the Ruby on Rails (RoR) framework.</p>
<blockquote><p><strong>What is ActionMailer?</strong><br />
Action Mailer is a framework for designing email-service layers.</p></blockquote>
<p>Sounds like ideal, does it? In order to use the package we will need to include it and also to create a class that extends the <code>ActionMailer::Base</code>, here is the code:</p>
<div class="hl-surround" ><div class="hl-main"><pre>require 'rubygems'
require_gem 'actionmailer'

#wrapper of RoR ActionMailer
class RubyBot &lt; ActionMailer::Base
  def receive(email)
    return email
  end
end</pre></div></div>
<p>Now we can convert the raw email string into a ruby object with the following call:</p>
<div class="hl-surround" style="height:28px;"><div class="hl-main"><pre>msg = RubyBot.receive(email)</pre></div></div>
<p>We can send signals such as <code>msg.subject</code> or <code>msg.parts</code> to the object to query the email&#8217;s information.</p>
<p>Before continuing with the rest of the script a word should be said regarding <strong>logging</strong> and <strong>configuration</strong>. After all, our <code>RubyBot</code> is <em>quick-and-dirty</em> script so for configuration we will use a hash at the begining of the file:</p>
<div class="hl-surround" ><div class="hl-main"><pre>#--------------------------------------- config
$options = {
  :myself =&gt; 'rubybot[_at_]nomejortu.com',
  :admin =&gt; 'etd[_at_]nomejortu.com',
  :subject =&gt; '[rubybot] notificacion:',
  :plugins_dir =&gt; './rbplugins',
  :pluginopt =&gt; {
    :tmpdir =&gt; './tmp',
    :logger =&gt; nil
  }
}
#--------------------------------------- /config</pre></div></div>
<p>All options are self explanatory except maybe two:</p>
<ul>
<li><strong>:plugins_dir</strong> will contain all of our plugins.</li>
<li><strong>:pluginopt</strong> contains the options that we will be passing to the plugins (mainly an object for logging and a temporary directory).</li>
</ul>
<p>For logging we are going to use ruby&#8217;s standard <a href="http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/classes/Logger.html">Logger</a> and we are going to store it&#8217;s output in a log file under the <strong>:plugins_dir</strong> directory. This is the code that initializes the logger:</p>
<div class="hl-surround" ><div class="hl-main"><pre>logfile = $options[:plugins_dir]+'/msg.log'
File.rm_f(logfile) if File.exist?(logfile)
log = Logger.new(logfile)

log.level = Logger::DEBUG
$options[:pluginopt][:logger] = log</pre></div></div>
<p>We are saving the name of the file for the last bit of the script where we attach the log file to the notification that <code>RubyBot</code> sends to the administrator. Also we are saving a reference to the logger in the configuration hash so we can pass it to out plugins.</p>
<p>Our script will use the subject line to decide which plugin should handle the request. The dispatching algorithm is show below:</p>
<div class="hl-surround" ><div class="hl-main"><pre>#2nd process the command (from email's subject)
module_name = msg.subject.split[0]
module_file = $options[:plugins_dir] + '/' + module_name + '.rb'
if (FileTest.exists?(module_file))
  log.info{ &quot;valid plugin found: #{module_name}&quot; }
  begin
    load module_file
    plugin = Kernel.const_get(module_name.capitalize + 'Plugin').new
    output = plugin.process(msg, $options[:pluginopt])
  rescue
    log.error{ &quot;error while processing command: #{$!}&quot; }
    log.debug{ $!.backtrace.join(&quot;n&quot;) }
    output = &quot;error while processing command: #{$!}&quot;
  end
else
  log.error {&quot;module not found in plugins dir (#{$options[:plugins_dir]})&quot;}
end</pre></div></div>
<p>A few things are going on in the previous piece of code. First we split the <code>subject</code> of the email and we take the first word as a module name. Then we try to determine if a file called <code><em>module_name</em>.rb</code> exists in the plugin directory. An error is logged if the file is not found. However, if we find the file we try to load the file and create an instance of the module:</p>
<div class="hl-surround" ><div class="hl-main"><pre>load module_file
plugin = Kernel.const_get(module_name.capitalize + 'Plugin').new</pre></div></div>
<p>For example, if our message&#8217;s subject is &#8220;<em>simple</em>&#8221; te previous lines will try to instantiate a copy of <code>SimplePlugin</code> from the file <code>./rbplugins/simple.rb</code>. If something goes wrong we capture and log the exception, otherwise we send the <code>.process</code> signal to the plugin passing the email and the options as arguments.</p>
<p>All <code>RubyBot</code>&#8217;s plugins should include the <code>Plugin</code> module as defined in <code>./rbplugins/plugin.rb</code>. The module has five methods and I will not give details of all of them here for the sake of clarity in the post. However, a brief description follows (<a href="http://en.wikipedia.org/wiki/MIME">MIME</a> stands for <em>Multipurpose Internet Mail Extensions</em>):-</p>
<ul>
<li><strong>part_filename</strong>: returns the filename that we should use for a given part in a <a href="http://en.wikipedia.org/wiki/MIME#Multipart_Messages">MIME multipart messages</a>.</li>
<li><strong>ext</strong>: given a <acronym title="Multipurpose Internet Mail Extensions">MIME</acronym> type the method returns a file extension.</li>
<li><strong>save</strong>: saves all the attachments of the email into a given folder.</li>
<li><strong>clear</strong>: given a folder, deletes it&#8217;s contents.</li>
</ul>
<p>The last and most interesting method is <code>process</code>. It will perform three operations: first save all the email&#8217;s attachments to the temporary folder, then process the body of the message looking for commands and finally delete the attachments from the temporary folder.</p>
<p>Depending on whether the email received is multipart or not a slightly different approach is needed to get the requested commands:</p>
<div class="hl-surround" ><div class="hl-main"><pre>#process the body, 1 command per line
if (@msg.parts.empty?)
  commands = @msg.body.split(/n/)
else
  commands = @msg.parts[0].body.split(/n/)
end</pre></div></div>
<p>Now we have an array that contains all the commands. The processing cycle goes as follows:</p>
<div class="hl-surround" ><div class="hl-main"><pre>commands.each do |cmdline|
  args = cmdline.split
  if (self.respond_to?(args[0]))
    begin
      @log.info( 'plugin' ) { &quot;[#{args[0]}] processed by #{args[0]} plugin. &quot; }
      out &lt;&lt; self.send(args[0], args[1,args.size-1])
    rescue
      @log.error('plugin') { &quot;error while processing command: #{$!}&quot; }
      @log.debug('plugin') { $!.backtrace.join(&quot;\n&quot;) }
      out &lt;&lt; &quot;error while processing command: #{$!}&quot;
    end
  else
    @log.error('plugin') { &quot;undefined command: #{args[0]}&quot; }
    out &lt;&lt; ''
  end
end</pre></div></div>
<p>Appart from the error handling the interesting calls are two: <code>self.respond_to?</code> and <code>self.send</code>. With the first one we check whether the plugin implements the requested command and with the second one we delegate the execution of the command to the implementation.</p>
<p>Following our previous example let&#8217;s say that we receive the following email:</p>
<div class="hl-surround" ><div class="hl-main"><pre>Subject: simple
[...]
echo hello world
echo good bye</pre></div></div>
<p><code>RubyBot</code> will parse the <code>Subject</code> line and will load and instantiate <code>SimplePlugin</code>. Then a <code>process</code> signal will be sent to the plugin. Since our example has no attachments, the <code>process</code> method (of the <code>Plugin</code> module) will just walk through the commands checking if we have implemented <code>echo</code> in <code>SimplePlugin module</code>. Provided that <code>SimpleCode</code> is defined as follows (in <code>./rbplugins/simple.rb</code>):</p>
<div class="hl-surround" ><div class="hl-main"><pre>require 'rbplugins/plugin'

class SimplePlugin
  include Plugin
  def echo(args)
    return &quot;good to go: #{args.join('|')}&quot;
  end
end</pre></div></div>
<p>The output of the commands issued above would be:</p>
<div class="hl-surround" ><div class="hl-main"><pre>good to go: hello|world
good to go: good|bye</pre></div></div>
<p>To comply with our last goal we need to add an extra method to the <code>RubyBot</code> class. The method <code>notification</code> will be used at the end of the script to send us a notification with all the details of the procession of our commands.</p>
<div class="hl-surround" style="height:280px;"><div class="hl-main"><pre>class RubyBot &lt; ActionMailer::Base
[...]
  def notification(msg, out, log)
    recipients $options[:admin]
    subject &quot;#{$options[:subject]} #{msg.subject}&quot;
    from $options[:myself]
    
    commands = 'empty'
    if (msg.parts.empty?)
      commands = msg.body
    else
      commands = msg.parts[0].body
    end

    body =&lt;&lt;EOF
    ------------------------------------
    Se ha recibido el mensaje anterior
    From: #{msg.from}
    Subject: #{msg.subject}
    Date: #{msg.date}
    Body:
      #{commands}
    Output: 
      #{out} 
    ------------------------------------
EOF
      
    part :content_type =&gt; 'text/plain', :body =&gt; body

    attachment :content_type =&gt; 'text/plain', :body =&gt; File.readlines(log).join(&quot;n&quot;)
  end
end</pre></div></div>
<p>The method creates a new email message with the recipient and subject specified by the options of the plugin. Then it creates a body for the email consisting of information regarding the received commands and at last attaches the log file created during the processing of the commands.</p>
<p>The last call of our script will be <code>RubyBot.deliver_notification(msg, output, logfile)</code>. ActionMailer&#8217;s <code>deliver_<em>something</em></code> will first call <code><em>something</em></code>, that should return an email object, and then will attempt to send the email to the specified recipients.</p>
<p>And that was all for today&#8230;</p>
<blockquote><p><strong>Pinky</strong>:  Why?  What are we going to do tomorrow night?<br />
<strong>Brain</strong>:  The same thing we do every night, Pinky.  Try to take over the world!</p></blockquote>
<img src="http://usefulfor.com/ruby/?ak_action=api_record_view&id=23&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://usefulfor.com/ruby/2007/08/07/ruby-bot-email-processing/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
