§ Simple XMPP relay

The idea here is simple: have a process monitor an UNIX pipe and broadcast anything it can read from the pipe to a set of targets, via XMPP. The beauty of this setup is that XMPP is common - your GMail and Facebook accounts both do XMPP. It also has a ready Ruby gem, so most of the work has already been done. Reading from the pipe can be rather easily replaced with reading from an UDP socket or whatever else suits your needs best. In times of always-online smartphones this provides a cheaper, easier and more fault-tolerant way than SMS.

§ The script

The bot script, which you should run as usual in the background:

#!/usr/bin/env ruby
#
# gtbot.rb - named pipe to GTalk/Jabber messages.
#
# (C) Piotr S. Staszewski, aka dRbiG, 2013
#

require 'rubygems'
require 'xmpp4r'

class GTBot
  attr_reader :jid, :pipe
  attr_accessor :targets

  def initialize(user, password, pipe, targets)
    raise ArgumentError, 'Targets has to be a non-empty Array of JIDs/Strings!' unless\
      targets.is_a? Array and targets.length > 0
    raise ArgumentError, 'Pipe has to be a readable named pipe!' unless\
      File.pipe? pipe and File.readable? pipe
   
    @jid = Jabber::JID.new(user + '/GTBOT')
    @client = Jabber::Client.new(@jid)

    @pipe = pipe
    @password = password
    @targets = targets

    @running = false
  end

  def connect
    return true if @client.is_connected?
    
    @client.connect
    @client.auth(@password)
    @client.send(Jabber::Presence.new.set_type(:available))

    @client.on_exception { sleep 5; connect }
  end

  def disconnect
    return true if @client.is_disconnected?

    @client.on_exception {}

    @client.send(Jabber::Presence.new.set_type(:unavailable))
    @client.close
  end

  def msg(text)
    return false unless @client.is_connected?

    text.gsub!(/[\n]+$/, '')
    @targets.collect {|t| @client.send(Jabber::Message.new(t, text)) }
  end
  
  def is_running?; @running; end

  def run!
    pipe = File.open(@pipe, 'r+')
    connect
    msg 'GTBot is online.'

    @running = true
    while @running; msg pipe.readline; end

    pipe.close
  end

  def stop!
    return true unless @running

    @running = false
    File.open(@pipe, 'w+') {|fd| fd.write("Closing session.\n") }
    sleep(0.5)

    msg 'GTBot going offline.'
    disconnect
  end
end

if __FILE__ == $0
  if ARGV.length != 1
    STDERR.puts 'Please specify config file path.'
    exit(2)
  end

  require 'yaml'

  begin
    config = YAML::load(File.read(ARGV.shift))
  rescue Exception => e
    STDERR.puts 'Error loading config file!'
    STDERR.puts e.to_s
    STDERR.puts e.backtrace.join("\n")
    exit(1)
  end

  gtbot = GTBot.new(config[:user], config[:password],
                    config[:pipe], config[:targets])

  pid = Process.fork do
    STDOUT.reopen(config[:log])
    STDERR.reopen(config[:log])

    Signal.trap('TERM') do
      gtbot.stop!
      exit(0)
    end

    gtbot.run!
  end

  STDERR.puts "Detached GTBot (PID #{pid})."
  File.open(config[:pidfile], 'w') {|fd| fd.puts pid }
  exit(0)
end

And the configuration file in YAML format, which you should of course edit:

---
:user: botaccount@gmail.com
:password: botpassword
:pipe: /path/to/gtpipe
:log: /path/to/gtbot.log
:pidfile: /path/to/gtbot.pid
:targets:
  - firsttarget@gmail.com
  - secondtarget@gmail.com

§ The result

I use it for getting the output of another ARP cache monitoring script, plus for some crucial log forwarding. Of course what you use it for is only a question of your needs and imagination.

We got contact.

comments powered by Disqus
Last update: 2013-01-05. » LINK » TXT » ATOM. Go to the top. Licence CC-BY-NC-SA.
If you find this interesting go ahead and leave a comment or add a star.
Any and all feedback is appreciated!