#!/usr/bin/env ruby require 'eventmachine' 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.2EventMachineSocketServer" $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"], ] module InterconnectionPointProtocolHandler @@connected_clients = Array.new @@broadcasts = Hash.new # default method that is being run on connection! def post_init # connection of someone starts here... @username = nil end ### getters def entered_username? !@username.nil? && !@username.empty? # then it's true end def username @username end def my_roles return $user_roles[@username] end def my_cmds return my_roles.collect {|v| $role_commands[v]}.flatten end # returns online users by default by searching through saved connections def ousers def online(user) if ousers.include? user return true else return false end end users = Array.new @@connected_clients.each {|c| users.push(c.username)} return users end # of ousers ### checkers getters def allowed_cmd(inputmessage) my_cmds.each{|c| #print "A #{c} B #{inputmessage}\n" if inputmessage =~ /^#{c}/ return true end } return false end ### actions def put_log(msg) puts "#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")}: #{msg}" end # searches through our hash of saved connections and writes them a messages. def write_user(input, username = nil) if not username username = @username end if connection = @@connected_clients.find { |c| c.username == username } sometime = "#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S %z")}" line = "#{sometime}: #{input}\n" connection.send_data line end end # of write_user # searches through roles and writes those users on their connections def write_role(role, input, *exempts) #find users with role users = Array.new $user_roles.each {|k, v| if v.include? role users.push(k) end } # find users that are online and inside Array "users" lala = Array.new users.each {|v| if ousers.include? v lala.push(v) end } # now write to them lala.each {|user| if not exempts.include? user write_user(input, user) end } end # of write_role() # what happens with what input?! def inputting(input) write_user("SYS You typed: #{input}") if input =~ /^([A-Z_]+)/ cmd = $1 end # now we have the cmd .. or not ;) if input =~ /^[A-Z_]+ (.+)/ payload = $1 end # now we can use payload if allowed_cmd(cmd) ### typical system commands follow if cmd == "PING" write_user("PONG") elsif cmd == "C" if payload =~ /^(.+)$/ write_role($default_role, "C #{@username}: #{$1}") else write_user("SYS Format is C ") end elsif cmd == "WHO" ousers.each {|ouser| write_user("WHO_RE #{ouser} ROLES: #{$user_roles[ouser].join(", ")}")} elsif cmd == "PART" write_user("SYS Goodbye '#{@username}'.") write_role($default_role, "PARTED User '#{@username}' just left the party.", @username) return "bye" ###now for the good stuff, broadcasting 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)/ write_user("SYS Network name #{network} is unknown: QWalt or QDEV allowed.") error = 1 end if not freqname =~ /^(-qw-)|(-spam-)|(-dev-)/ write_user("SYS Frequency name is unknown #{freqname}") error = 1 end if not source =~ /^(#.+)|(qw:\/\/)|(http:\/\/)/ write_user("SYS Source string is not in the form ^(#.+)|(qw:\/\/)|(http:\/\/) was: #{source}") error = 1 end if not nickname =~ /^.+/ write_user("SYS Nickname string is not in the form ^.+ #{nickname}") error = 1 end if not text =~ /^.+/ write_user("SYS Message string is not in the form ^.+ #{text}") error = 1 end else # of check syntax write_user("SYS Command format is REQ_BC ,,,'',''") error = 1 end if error == 0 # send REQ_BC to the corresponding role. bcid = Digest::MD5.hexdigest("%d %s %s %s %s %s" % [Time.now.utc.to_i, network, freqname, source, nickname, text]) # so it only reaches the issuer of REQ_BC write_user("BC_ID #{bcid} for: #{network},#{freqname},#{source}") @@broadcasts[bcid] = @username finalmessage = "BC %s %s,%s,%s,'%s','%s'" % [bcid, network, freqname, source, nickname, text] write_role("broadcast", finalmessage, @username) # write to the role with the exempt of myself. end #end of REQ_BC here.. 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] write_user("SYS Broadcast reply bcid: #{bcid} underway to #{to_user}.") write_user("#{cmd} #{payload}", to_user) else write_user("SYS Broadcast reply bcid: #{bcid} underway.") write_role("broadcast", "#{cmd} #{payload}", @username) end else # of bc_re format check put_log "SYS Format is BC_RE =,=" end end else # of if allowed command write_user("SYS Command #{input} not allowed, your commands are: #{my_cmds.join(", ")}.") end # of if allowed command end # of inputting! # default method that is being run on receiving data! def receive_data(data) # connection receives a line on the server end if not entered_username? # oh, it seems, it's a new user # this is the user name coming in! username = data.chomp if not $user_roles.any? {|k, v| k.include? username} put_log "SYS User '#{username}' unknown to me. Saying bye." send_data "SYS User '#{username}' unknown to me. Bye." # disconnect him close_connection else @username = username @@connected_clients.push(self) put_log("'#{@username}' connected. Online now: #{ousers.join(", ")}") write_user("HELLO Hi user '#{@username}'! How are you? I'm '#{$version}'") write_user("ROLES #{my_roles.join(", ")}") write_user("COMMANDS #{my_cmds.join(", ")}") write_role($default_role, "JOINED User '#{@username}' just joined the party.", @username) end else # of username not known (first connect) # it's alive! inputter = inputting(data.chomp) if inputter == "bye" close_connection end end # of username not known end # default method that is being run on disconnection! def unbind # a connection goes bye bye if @username @@connected_clients.delete(self) put_log "'#{@username}' disconnected. Online now: #{ousers.join(", ")}" end end end def main # #run: Note that this will block current thread. EventMachine.run { EventMachine.start_server "0.0.0.0", 7338, InterconnectionPointProtocolHandler } end main