em_server.rb 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #!/usr/bin/env ruby
  2. require 'eventmachine'
  3. require 'digest/md5'
  4. # .. allowed commands .. ROLES = CAPABILITIES
  5. # normal users have ROLE broadcast. Roles are defined on a per-user basis .. in a config.
  6. $version = "0.2EventMachineSocketServer"
  7. $debug = 0
  8. $role_commands = Hash[
  9. "everyone" => ["PING", "WHO", "C", "PART"],
  10. "broadcast_admin" => ["BC_ID", "BC", "BC_ENDCOUNT"],
  11. "broadcast" => ["REQ_BC", "BC_RE"],
  12. "specbot_admin" => ["REQ_ASSIGN", "REQ_UNASSIGN", "REQ_PING", "REQ_ASSIGNMENTS"],
  13. "specbot" => ["ASSIGN_RE", "UNASSIGN_RE", "PING_RE", "ASSIGNMENTS_RE"],
  14. ]
  15. $default_role = "everyone"
  16. # which role is talking to which role?
  17. # effectively it says: this (local) command is sent to that (remote) topic .. that certain topic is read by that user with that role.
  18. $role_dialogs = Hash[
  19. "everyone" => ["everyone"],
  20. "broadcast_admin" => ["broadcast"],
  21. "broadcast" => ["broadcast_admin"],
  22. "specbot_admin" => ["specbot"],
  23. "specbot" => ["specbot_admin"],
  24. ]
  25. $user_roles = Hash[
  26. "paul_dev_eggdrop" => ["everyone", "broadcast"],
  27. "paul_eggdrop" => ["everyone", "broadcast"],
  28. "paul_dev_specbot" => ["everyone", "broadcast", "specbot"],
  29. "paul_specbot" => ["everyone", "broadcast", "specbot"],
  30. "qw.nu" => ["everyone", "broadcast"],
  31. "qw.nu_poster" => ["everyone", "broadcast"],
  32. "mihawk_dev_specbot" => ["everyone", "broadcast", "specbot"],
  33. "mihawk_specbot" => ["everyone", "broadcast", "specbot"],
  34. "central_brain" => ["everyone", "broadcast_admin", "specbot_admin"],
  35. ]
  36. module InterconnectionPointProtocolHandler
  37. @@connected_clients = Array.new
  38. @@broadcasts = Hash.new
  39. # default method that is being run on connection!
  40. def post_init # connection of someone starts here...
  41. @username = nil
  42. end
  43. ### getters
  44. def entered_username?
  45. !@username.nil? && !@username.empty? # then it's true
  46. end
  47. def username
  48. @username
  49. end
  50. def my_roles
  51. return $user_roles[@username]
  52. end
  53. def my_cmds
  54. return my_roles.collect {|v| $role_commands[v]}.flatten
  55. end
  56. # returns online users by default by searching through saved connections
  57. def ousers
  58. def online(user)
  59. if ousers.include? user
  60. return true
  61. else
  62. return false
  63. end
  64. end
  65. users = Array.new
  66. @@connected_clients.each {|c| users.push(c.username)}
  67. return users
  68. end # of ousers
  69. ### checkers getters
  70. def allowed_cmd(inputmessage)
  71. my_cmds.each{|c|
  72. #print "A #{c} B #{inputmessage}\n"
  73. if inputmessage =~ /^#{c}/
  74. return true
  75. end
  76. }
  77. return false
  78. end
  79. ### actions
  80. def put_log(msg)
  81. puts "#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")}: #{msg}"
  82. end
  83. # searches through our hash of saved connections and writes them a messages.
  84. def write_user(input, username = nil)
  85. if not username
  86. username = @username
  87. end
  88. if connection = @@connected_clients.find { |c| c.username == username }
  89. sometime = "#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S %z")}"
  90. line = "#{sometime}: #{input}\n"
  91. connection.send_data line
  92. end
  93. end # of write_user
  94. # searches through roles and writes those users on their connections
  95. def write_role(role, input, *exempts)
  96. #find users with role
  97. users = Array.new
  98. $user_roles.each {|k, v|
  99. if v.include? role
  100. users.push(k)
  101. end
  102. }
  103. # find users that are online and inside Array "users"
  104. lala = Array.new
  105. users.each {|v|
  106. if ousers.include? v
  107. lala.push(v)
  108. end
  109. }
  110. # now write to them
  111. lala.each {|user|
  112. if not exempts.include? user
  113. write_user(input, user)
  114. end
  115. }
  116. end # of write_role()
  117. # what happens with what input?!
  118. def inputting(input)
  119. write_user("SYS You typed: #{input}")
  120. if input =~ /^([A-Z_]+)/
  121. cmd = $1
  122. end
  123. # now we have the cmd .. or not ;)
  124. if input =~ /^[A-Z_]+ (.+)/
  125. payload = $1
  126. end
  127. # now we can use payload
  128. if allowed_cmd(cmd)
  129. ### typical system commands follow
  130. if cmd == "PING"
  131. write_user("PONG")
  132. elsif cmd == "C"
  133. if payload =~ /^(.+)$/
  134. write_role($default_role, "C #{@username}: #{$1}")
  135. else
  136. write_user("SYS Format is C <chattext>")
  137. end
  138. elsif cmd == "WHO"
  139. ousers.each {|ouser| write_user("WHO_RE #{ouser} ROLES: #{$user_roles[ouser].join(", ")}")}
  140. elsif cmd == "PART"
  141. write_user("SYS Goodbye '#{@username}'.")
  142. write_role($default_role, "PARTED User '#{@username}' just left the party.", @username)
  143. return "bye"
  144. ###now for the good stuff, broadcasting
  145. elsif cmd == "REQ_BC"
  146. # help with format .. but send the raw payload
  147. if payload =~ /^(.+),(.+),(.+),'(.+)','(.+)'$/
  148. # n f s ni txt
  149. error = 0
  150. # $&
  151. # 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
  152. network = $1
  153. freqname = $2
  154. source = $3
  155. nickname = $4
  156. text = $5
  157. if not network =~ /^(QWalt)|(QDEV)/
  158. write_user("SYS Network name #{network} is unknown: QWalt or QDEV allowed.")
  159. error = 1
  160. end
  161. if not freqname =~ /^(-qw-)|(-spam-)|(-dev-)/
  162. write_user("SYS Frequency name is unknown #{freqname}")
  163. error = 1
  164. end
  165. if not source =~ /^(#.+)|(qw:\/\/)|(http:\/\/)/
  166. write_user("SYS Source string is not in the form ^(#.+)|(qw:\/\/)|(http:\/\/) was: #{source}")
  167. error = 1
  168. end
  169. if not nickname =~ /^.+/
  170. write_user("SYS Nickname string is not in the form ^.+ #{nickname}")
  171. error = 1
  172. end
  173. if not text =~ /^.+/
  174. write_user("SYS Message string is not in the form ^.+ #{text}")
  175. error = 1
  176. end
  177. else # of check syntax
  178. write_user("SYS Command format is REQ_BC <network>,<frequency>,<source/channel>,'<nickname>','<message>'")
  179. error = 1
  180. end
  181. if error == 0
  182. # send REQ_BC to the corresponding role.
  183. bcid = Digest::MD5.hexdigest("%d %s %s %s %s %s" % [Time.now.utc.to_i, network, freqname, source, nickname, text])
  184. # so it only reaches the issuer of REQ_BC
  185. write_user("BC_ID #{bcid} for: #{network},#{freqname},#{source}")
  186. @@broadcasts[bcid] = @username
  187. finalmessage = "BC %s %s,%s,%s,'%s','%s'" % [bcid, network, freqname, source, nickname, text]
  188. write_role("broadcast", finalmessage, @username) # write to the role with the exempt of myself.
  189. end
  190. #end of REQ_BC here..
  191. elsif cmd == "BC_RE"
  192. if payload =~ /^(.+) (.+)=(\d+),(.+)=(\d+)$/
  193. bcid = $1
  194. user_string = $2
  195. user_count = $3
  196. item_string = $4
  197. item_count = $5
  198. payload = "%s %s=%d,%s=%d" % [bcid, user_string, user_count, item_string, item_count]
  199. # according bcid it is possible to get the originating user.. so send only to his topic!
  200. if @@broadcasts[bcid]
  201. to_user = @@broadcasts[bcid]
  202. write_user("SYS Broadcast reply bcid: #{bcid} underway to #{to_user}.")
  203. write_user("#{cmd} #{payload}", to_user)
  204. else
  205. write_user("SYS Broadcast reply bcid: #{bcid} underway.")
  206. write_role("broadcast", "#{cmd} #{payload}", @username)
  207. end
  208. else # of bc_re format check
  209. put_log "SYS Format is BC_RE <bcid> <userstring>=<usercount>,<itemstring>=<itemcount>"
  210. end
  211. end
  212. else # of if allowed command
  213. write_user("SYS Command #{input} not allowed, your commands are: #{my_cmds.join(", ")}.")
  214. end # of if allowed command
  215. end # of inputting!
  216. # default method that is being run on receiving data!
  217. def receive_data(data) # connection receives a line on the server end
  218. if not entered_username? # oh, it seems, it's a new user
  219. # this is the user name coming in!
  220. username = data.chomp
  221. if not $user_roles.any? {|k, v| k.include? username}
  222. put_log "SYS User '#{username}' unknown to me. Saying bye."
  223. send_data "SYS User '#{username}' unknown to me. Bye."
  224. # disconnect him
  225. close_connection
  226. else
  227. @username = username
  228. @@connected_clients.push(self)
  229. put_log("'#{@username}' connected. Online now: #{ousers.join(", ")}")
  230. write_user("HELLO Hi user '#{@username}'! How are you? I'm '#{$version}'")
  231. write_user("ROLES #{my_roles.join(", ")}")
  232. write_user("COMMANDS #{my_cmds.join(", ")}")
  233. write_role($default_role, "JOINED User '#{@username}' just joined the party.", @username)
  234. end
  235. else # of username not known (first connect)
  236. # it's alive!
  237. inputter = inputting(data.chomp)
  238. if inputter == "bye"
  239. close_connection
  240. end
  241. end # of username not known
  242. end
  243. # default method that is being run on disconnection!
  244. def unbind # a connection goes bye bye
  245. if @username
  246. @@connected_clients.delete(self)
  247. put_log "'#{@username}' disconnected. Online now: #{ousers.join(", ")}"
  248. end
  249. end
  250. end
  251. def main
  252. # #run: Note that this will block current thread.
  253. EventMachine.run {
  254. EventMachine.start_server "0.0.0.0", 7338, InterconnectionPointProtocolHandler
  255. }
  256. end
  257. main