#!/usr/bin/ruby # vim: expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent: require 'rubygems' require 'stomp' require 'digest/md5' # .. allowed commands .. ROLES = CAPABILITIES # normal users have ROLE broadcast. Roles are defined on a per-user basis .. in a config. $version = "0.11big_yadda" $debug = 0 $role_commands = Hash[ "everyone" => ["PING", "WHO", "C", "PART"], "broadcast_admin" => ["BC_ID", "BC", "BC_ENDCOUNT"], "broadcast" => ["REQ_BC", "BC_RE"], "specbot_admin" => ["REQ_ASSIGN", "REQ_UNASSIGN", "REQ_PING", "REQ_ASSIGNMENTS"], "specbot" => ["ASSIGN_RE", "UNASSIGN_RE", "PING_RE", "ASSIGNMENTS_RE"], ] $default_role = "everyone" # which role is talking to which role? # effectively it says: this (local) command is sent to that (remote) topic .. that certain topic is read by that user with that role. $role_dialogs = Hash[ "everyone" => ["everyone"], "broadcast_admin" => ["broadcast"], "broadcast" => ["broadcast_admin"], "specbot_admin" => ["specbot"], "specbot" => ["specbot_admin"], ] $user_roles = Hash[ "paul_dev_eggdrop" => ["everyone", "broadcast"], "paul_eggdrop" => ["everyone", "broadcast"], "paul_dev_specbot" => ["everyone", "broadcast", "specbot"], "paul_specbot" => ["everyone", "broadcast", "specbot"], "qw.nu" => ["everyone", "broadcast"], "qw.nu_poster" => ["everyone", "broadcast"], "mihawk_dev_specbot" => ["everyone", "broadcast", "specbot"], "mihawk_specbot" => ["everyone", "broadcast", "specbot"], "central_brain" => ["everyone", "broadcast_admin", "specbot_admin"], ] STDOUT.sync = true def put_debug(msg) if $debug == 1 puts msg end end def put_log(msg) puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S %z")}: #{msg}" end def my_subscribe(client, topic) put_debug "Subscribing to #{topic}" client.subscribe("/topic/#{topic}") end # check if current text is readable for user def check_allow_processing(text, user) ok = false if not text =~ /^from:#{user}:/ put_debug "may be a command: #{text}" ok = true end if text =~ /^!to:#{user}:/ put_debug "not for #{user} #{text}" ok = false elsif text =~ /^to:#{user}:/ put_debug "it is for #{user} #{text}" ok = true end return ok end # consuming process with a loop def consuming() put_debug "SYS Starting consuming/reading thread\n" client = Stomp::Connection.new("", "", "localhost", 61613, true) user = $user my_subscribe(client, "messages") my_subscribe(client, "#{user}-replies") # for each of our own roles, we subscribe $my_roles.each { |role| my_subscribe(client, "#{role}") } loop do begin msg = client.receive if check_allow_processing(msg.body, $user) #put_debug "msg.headers #{msg.headers["reply-to"]}\n" #put_debug "msg_array #{msg_array}\n" #put_log "RECEIVED_DBG: #{msg.body}" message = msg.body #from:#{$user}: #{cmd} #{payload} #put_log "message #{message}" message =~ /^([!fromto]+):([a-zA-Z\._]+): (.+)$/ if not $& == nil m_sentmode = $1 m_user = $2 message = $3 #put_log "sentmode #{m_sentmode}" #put_log "m_user #{m_user}" #put_log "message #{message}" end message =~ /^([A-Z_]+) (.*)/ if not $& == nil m_cmd = $1 m_pload = $2 end if m_cmd =~ /^JOIN$/ put_log "SYS User '#{m_user}' just joined the party." elsif m_cmd =~ /^C$/ put_log "C #{m_user}: #{m_pload}" elsif m_cmd =~ /^WHO$/ reply_to_msg(msg, "WHO_RE", "'#{$user}' on '#{$version}' ROLES: #{$user_roles[$user].join(", ")}") elsif m_cmd =~ /^PART$/ put_log "PARTED User '#{m_user}' just left the party." elsif m_cmd =~ /^REQ_BC$/ # this is for central_brain to process... so central needs to know from who. put_log "from:#{m_user}: #{m_cmd} #{m_pload}" else # general output of received commands on stomp on this connection (ssh) put_log "#{m_cmd} #{m_pload}" if m_cmd =~ /^BC$/ #sending a fake reply. if user == "paul_dev_eggdrop" put_log "debug: sending fake reply based on #{msg.headers["reply-to"]}" m_pload =~ /^(.+?) / bcid = $1 reply_to_msg(msg, "BC_RE", "#{bcid} Squirrels=2,Nuts=5") end end end else # of check_allow_processing put_debug "RECEIVED from myself. Ignoring." end # of check_allow_processing rescue put_debug "hum?" retry end end end def send(path, inputmsg, *header) begin finalmessage = "" Timeout::timeout(2) do stomp = Stomp::Client.new("", "", "localhost", 61613) finalmessage = "%s" % [inputmsg] if header != "" stomp.publish(path, finalmessage, *header) else stomp.publish(path, finalmessage) end stomp.close end rescue Timeout::Error put_debug "Failed to send within the 2 second timeout" exit 1 else put_debug "SENT to #{path}: #{finalmessage}" end end def allowed_cmd(inputmessage) $my_cmds.each{|c| #print "A #{c} B #{inputmessage}\n" if inputmessage =~ /^#{c}/ return true end } return false end def find_role_by_cmd(cmd) $my_roles.each { |role| #print "wah: #{role}" $role_commands[role].each { |c| if c == cmd #print "wOOh: #{role}" return role end } } return false end def find_dest_by_role(role) $role_dialogs.each { |srcrole, dstrole| if srcrole == role return dstrole end } return false end def send_cmd(cmd, payload, system=false, sendmode="from", to_user=$user) #find role by cmd #$role_commands.each #$my_cmds = $my_roles.collect {|v| $role_commands[v]} if system role = $default_role else role = find_role_by_cmd(cmd) end dest = find_dest_by_role(role) put_debug "cmd: #{cmd}" put_debug "role: #{role}" put_debug "dest: #{dest}" put_debug "payload: #{payload}" if sendmode == "from" send("/topic/#{dest}", "from:#{to_user}: #{cmd} #{payload}","reply-to" => "/topic/#{$user}-replies") elsif sendmode == "to" && to_user != "" dest = "#{to_user}-replies" send("/topic/#{dest}", "#{cmd} #{payload}","reply-to" => "/topic/#{$user}-replies") elsif sendmode == "!to" && to_user != "" send("/topic/#{dest}", "!to:#{to_user}: #{cmd} #{payload}","reply-to" => "/topic/#{$user}-replies") end end def reply_to_msg(msg, cmd, payload) if msg.headers["reply-to"] send(msg.headers["reply-to"], "from:#{$user}: #{cmd} #{payload}") end end def main raise "ARGV[0] must be a userstring - no numbers!" unless ARGV[0] =~ /^[a-zA-Z\._]+$/ $user = "%s" % [ARGV[0]] if not $user_roles.any? {|k, v| k.include? $user} put_log "SYS User unknown to me." exit end $my_roles = $user_roles[$user] $my_cmds = $my_roles.collect {|v| $role_commands[v]}.flatten $broadcasts = Hash.new put_log "HELLO Hi user '#{$user}'! How are you? I'm #{$version}" put_log "ROLES #{$my_roles.join(", ")}" put_log "COMMANDS #{$my_cmds.join(", ")}" send_cmd("JOIN", "Hi, I'm all in.", true) thread_consuming = Thread.new {consuming} #put_log "SYS Going into input loop" inputmessage = "" while inputmessage != "PART" inputmessage = "" cmd = "" payload = "" sendmode = "" to_user = "" inputmessage = STDIN.gets.chomp if check_allow_processing(inputmessage, $user) if inputmessage =~ /^([!]?to):([a-zA-Z_\.]+): (.+)/ sendmode = $1 to_user = $2 inputmessage = $3 put_debug "sendmode: #{sendmode}" put_debug "to_user: #{to_user}" end if inputmessage =~ /^([A-Z_]+)/ cmd = $1 end # now we have the cmd .. or not ;) if inputmessage =~ /^[A-Z_]+ (.+)/ payload = $1 end # now we can use payload if allowed_cmd(cmd) if cmd == "PING" put_log "PONG" elsif cmd == "REQ_BC" # help with format .. but send the raw payload if payload =~ /^(.+),(.+),(.+),'(.+)','(.+)'$/ # n f s ni txt error = 0 # $& # The string matched by the last successful pattern match in this scope, or nil if the last pattern match failed. (Mnemonic: like & in some editors.) This variable is r network = $1 freqname = $2 source = $3 nickname = $4 text = $5 if not network =~ /^(QWalt)|(QDEV)/ put_log "SYS Network name #{network} is unknown: QWalt or QDEV allowed." error = 1 end if not freqname =~ /^(-qw-)|(-spam-)|(-dev-)/ put_log "SYS Frequency name is unknown #{freqname}" error = 1 end if not source =~ /^(#.+)|(qw:\/\/)|(http:\/\/)/ put_log "SYS Source string is not in the form ^(#.+)|(qw:\/\/)|(http:\/\/) was: #{source}" error = 1 end if not nickname =~ /^.+/ put_log "SYS Nickname string is not in the form ^.+ #{nickname}" error = 1 end if not text =~ /^.+/ put_log "SYS Message string is not in the form ^.+ #{text}" error = 1 end else # of check syntax put_log "SYS Command format is REQ_BC ,,,'',''" error = 1 end if error == 0 payload = "%s,%s,%s,'%s','%s'" % [network, freqname, source, nickname, text] send_cmd(cmd, payload) # send REQ_BC to the corresponding role. end elsif cmd == "BC_RE" if payload =~ /^(.+) (.+)=(\d+),(.+)=(\d+)$/ bcid = $1 user_string = $2 user_count = $3 item_string = $4 item_count = $5 payload = "%s %s=%d,%s=%d" % [bcid, user_string, user_count, item_string, item_count] # according bcid it is possible to get the originating user.. so send only to his topic! if $broadcasts[bcid] to_user = $broadcasts[bcid] put_log "SYS Broadcast reply bcid: #{bcid} underway. To: '#{to_user}'." send("/topic/#{to_user}-replies", "#{cmd} #{payload}") #send_cmd(cmd, payload) .. and thus: central_brain has to collect the BC_REs else put_log "SYS Broadcast reply bcid: #{bcid} underway." send("/topic/broadcast", "#{cmd} #{payload}") #send_cmd(cmd, payload) .. and thus: central_brain has to collect the BC_REs end else put_log "SYS Format is BC_RE =,=" end elsif cmd == "C" if payload =~ /^(.+)$/ put_log "C #{$user}: #{$1}" send_cmd("C", "#{$1}") else put_log "SYS Format is C " end elsif cmd == "WHO" put_log "SYS Asking who's here." send_cmd(cmd, "is here?") elsif cmd == "PART" put_log "SYS Goodbye '#{$user}'." send_cmd(cmd, "The party was fun.") else # universal sender for other allowed commands. if sendmode && to_user put_debug "sendmode: #{sendmode} to_user: #{to_user}" send_cmd(cmd, payload, false, sendmode, to_user) else send_cmd(cmd, payload) end end # of if command == xyz else # of if allowed command put_log "SYS Command #{inputmessage} not allowed, your commands are: #{$my_cmds.join(", ")}." end # of if allowed command end # of if we should process it end # of while input != "part" end # of main main