connection.rb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. #!/usr/bin/ruby
  2. # vim: expandtab tabstop=2 shiftwidth=2 softtabstop=2 autoindent:
  3. require 'rubygems'
  4. require 'stomp'
  5. require 'digest/md5'
  6. # .. allowed commands .. ROLES = CAPABILITIES
  7. # normal users have ROLE broadcast. Roles are defined on a per-user basis .. in a config.
  8. $version = "0.10big_yadda"
  9. $role_commands = Hash[
  10. "everyone" => ["PING", "WHO", "C", "PART"],
  11. "broadcast_admin" => ["BC_ID", "BC", "BC_ENDCOUNT"],
  12. "broadcast" => ["REQ_BC", "BC_RE"],
  13. "specbot_admin" => ["REQ_ASSIGN", "REQ_UNASSIGN", "REQ_PING", "REQ_ASSIGNMENTS"],
  14. "specbot" => ["ASSIGN_RE", "UNASSIGN_RE", "PING_RE", "ASSIGNMENTS_RE"],
  15. ]
  16. $default_role = "everyone"
  17. # which role is talking to which role?
  18. # effectively it says: this (local) command is sent to that (remote) topic .. that certain topic is read by that user with that role.
  19. $role_dialogs = Hash[
  20. "everyone" => ["everyone"],
  21. "broadcast_admin" => ["broadcast"],
  22. "broadcast" => ["broadcast_admin"],
  23. "specbot_admin" => ["specbot"],
  24. "specbot" => ["specbot_admin"],
  25. ]
  26. $user_roles = Hash[
  27. "paul_dev_eggdrop" => ["everyone", "broadcast"],
  28. "paul_eggdrop" => ["everyone", "broadcast"],
  29. "paul_dev_specbot" => ["everyone", "broadcast", "specbot"],
  30. "paul_specbot" => ["everyone", "broadcast", "specbot"],
  31. "qw.nu" => ["everyone", "broadcast"],
  32. "qw.nu_poster" => ["everyone", "broadcast"],
  33. "mihawk_dev_specbot" => ["everyone", "broadcast", "specbot"],
  34. "mihawk_specbot" => ["everyone", "broadcast", "specbot"],
  35. "central_brain" => ["everyone", "broadcast_admin", "specbot_admin"],
  36. ]
  37. STDOUT.sync = true
  38. def put_debug(msg)
  39. if $debug == 1
  40. puts msg
  41. end
  42. end
  43. def put_log(msg)
  44. puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S %z")}: #{msg}"
  45. end
  46. def my_subscribe(client, topic)
  47. put_debug "Subscribing to #{topic}"
  48. client.subscribe("/topic/#{topic}")
  49. end
  50. # check if current text is readable for user
  51. def check_allow_processing(text, user)
  52. ok = false
  53. if not text =~ /^from:#{user}:/
  54. put_debug "may be a command: #{text}"
  55. ok = true
  56. end
  57. if text =~ /^!to:#{user}:/
  58. put_debug "not for #{user} #{text}"
  59. ok = false
  60. elsif text =~ /^to:#{user}:/
  61. put_debug "it is for #{user} #{text}"
  62. ok = true
  63. end
  64. return ok
  65. end
  66. # consuming process with a loop
  67. def consuming()
  68. put_debug "SYS Starting consuming/reading thread\n"
  69. client = Stomp::Connection.new("", "", "localhost", 61613, true)
  70. user = $user
  71. my_subscribe(client, "messages")
  72. my_subscribe(client, "#{user}-replies")
  73. # for each of our own roles, we subscribe
  74. $my_roles.each {
  75. |role|
  76. my_subscribe(client, "#{role}")
  77. }
  78. loop do
  79. begin
  80. msg = client.receive
  81. if check_allow_processing(msg.body, $user)
  82. #put_debug "msg.headers #{msg.headers["reply-to"]}\n"
  83. #put_debug "msg_array #{msg_array}\n"
  84. #put_log "RECEIVED_DBG: #{msg.body}"
  85. message = msg.body
  86. #from:#{$user}: #{cmd} #{payload}
  87. #put_log "msg #{message}"
  88. message =~ /^([!fromto]+):([a-zA-Z\._]+): (.+)$/
  89. if not $& == nil
  90. m_sentmode = $1
  91. m_user = $2
  92. message = $3
  93. #put_log "sentmode #{m_sentmode}"
  94. #put_log "m_user #{m_user}"
  95. #put_log "message #{message}"
  96. end
  97. message =~ /^([A-Z_]+) (.*)/
  98. if not $& == nil
  99. m_cmd = $1
  100. m_pload = $2
  101. end
  102. if m_cmd =~ /^JOIN$/
  103. put_log "SYS User '#{m_user}' just joined the party."
  104. elsif m_cmd =~ /^C$/
  105. put_log "C #{m_user}: #{m_pload}"
  106. elsif m_cmd =~ /^WHO$/
  107. reply_to_msg(msg, "WHO_RE", "#{$user}' on '#{$version}' ROLES: #{$user_roles[$user].join(", ")}")
  108. elsif m_cmd =~ /^PART$/
  109. put_log "PARTED User '#{m_user}' just left the party."
  110. elsif m_cmd =~ /^REQ_BC$/
  111. # this is for central_brain to process... so central needs to know from who.
  112. put_log "from:#{m_user}: #{m_cmd} #{m_pload}"
  113. else
  114. # general output of received commands on stomp on this connection (ssh)
  115. put_log "#{m_cmd} #{m_pload}"
  116. if m_cmd =~ /^BC$/
  117. #sending a fake reply.
  118. if user == "paul_dev_eggdrop"
  119. put_log "debug: sending fake reply based on #{msg.headers["reply-to"]}"
  120. m_pload =~ /^(.+?) /
  121. bcid = $1
  122. reply_to_msg(msg, "BC_RE", "#{bcid} Squirrels=2,Nuts=5")
  123. end
  124. end
  125. end
  126. else
  127. put_debug "RECEIVED from myself. Ignoring."
  128. end
  129. rescue
  130. put_debug "hum?"
  131. retry
  132. end
  133. end
  134. end
  135. def send(path, inputmsg, *header)
  136. begin
  137. finalmessage = ""
  138. Timeout::timeout(2) do
  139. stomp = Stomp::Client.new("", "", "localhost", 61613)
  140. finalmessage = "%s" % [inputmsg]
  141. if header != ""
  142. stomp.publish(path, finalmessage, *header)
  143. else
  144. stomp.publish(path, finalmessage)
  145. end
  146. stomp.close
  147. end
  148. rescue Timeout::Error
  149. put_debug "Failed to send within the 2 second timeout"
  150. exit 1
  151. else
  152. put_debug "SENT to #{path}: #{finalmessage}"
  153. end
  154. end
  155. def allowed_cmd(inputmessage)
  156. $my_cmds.each{|c|
  157. #print "A #{c} B #{inputmessage}\n"
  158. if inputmessage =~ /^#{c}/
  159. return true
  160. end
  161. }
  162. return false
  163. end
  164. def find_role_by_cmd(cmd)
  165. $my_roles.each {
  166. |role|
  167. #print "wah: #{role}"
  168. $role_commands[role].each {
  169. |c|
  170. if c == cmd
  171. #print "wOOh: #{role}"
  172. return role
  173. end
  174. }
  175. }
  176. return false
  177. end
  178. def find_dest_by_role(role)
  179. $role_dialogs.each {
  180. |srcrole, dstrole|
  181. if srcrole == role
  182. return dstrole
  183. end
  184. }
  185. return false
  186. end
  187. def send_cmd(cmd, payload, system=false, sendmode="from", to_user=$user)
  188. #find role by cmd
  189. #$role_commands.each
  190. #$my_cmds = $my_roles.collect {|v| $role_commands[v]}
  191. if system
  192. role = $default_role
  193. else
  194. role = find_role_by_cmd(cmd)
  195. end
  196. dest = find_dest_by_role(role)
  197. put_debug "cmd: #{cmd}"
  198. put_debug "role: #{role}"
  199. put_debug "dest: #{dest}"
  200. put_debug "payload: #{payload}"
  201. if sendmode == "from"
  202. send("/topic/#{dest}", "from:#{to_user}: #{cmd} #{payload}","reply-to" => "/topic/#{$user}-replies")
  203. elsif sendmode == "to" && to_user != ""
  204. dest = "#{to_user}-replies"
  205. send("/topic/#{dest}", "#{cmd} #{payload}","reply-to" => "/topic/#{$user}-replies")
  206. elsif sendmode == "!to" && to_user != ""
  207. send("/topic/#{dest}", "!to:#{to_user}: #{cmd} #{payload}","reply-to" => "/topic/#{$user}-replies")
  208. end
  209. end
  210. def reply_to_msg(msg, cmd, payload)
  211. if msg.headers["reply-to"]
  212. send(msg.headers["reply-to"], "from:#{$user}: #{cmd} #{payload}")
  213. end
  214. end
  215. def main
  216. raise "ARGV[0] must be a userstring - no numbers!" unless ARGV[0] =~ /^[a-zA-Z_\.]+$/
  217. $user = "%s" % [ARGV[0]]
  218. if not $user_roles.any? {|k, v| k.include? $user}
  219. put_log "SYS User unknown to me."
  220. exit
  221. end
  222. $my_roles = $user_roles[$user]
  223. $my_cmds = $my_roles.collect {|v| $role_commands[v]}.flatten
  224. $debug = 0
  225. $broadcasts = Hash.new
  226. put_log "HELLO Hi user '#{$user}'! How are you? I'm #{$version}"
  227. put_log "ROLES #{$my_roles.join(", ")}"
  228. put_log "COMMANDS #{$my_cmds.join(", ")}"
  229. send_cmd("JOIN", "Hi, I'm all in.", true)
  230. thread_consuming = Thread.new {consuming}
  231. #put_log "SYS Going into input loop"
  232. inputmessage = ""
  233. while inputmessage != "PART"
  234. inputmessage = ""
  235. cmd = ""
  236. payload = ""
  237. sendmode = ""
  238. to_user = ""
  239. inputmessage = STDIN.gets.chomp
  240. if check_allow_processing(inputmessage, $user)
  241. if inputmessage =~ /^([!]?to):([a-zA-Z_\.]+): (.+)/
  242. sendmode = $1
  243. to_user = $2
  244. inputmessage = $3
  245. put_debug "sendmode: #{sendmode}"
  246. put_debug "to_user: #{to_user}"
  247. end
  248. if inputmessage =~ /^([A-Z_]+)/
  249. cmd = $1
  250. end
  251. # now we have the cmd .. or not ;)
  252. if inputmessage =~ /^[a-zA-Z_]+ (.+)/
  253. payload = $1
  254. end
  255. # now we can use payload
  256. if allowed_cmd(cmd)
  257. if cmd == "PING"
  258. put_log "PONG"
  259. elsif cmd == "REQ_BC"
  260. # help with format .. but send the raw payload
  261. if payload =~ /^(.+),(.+),(.+),'(.+)','(.+)'$/
  262. # n f s ni txt
  263. error = 0
  264. # $&
  265. # 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
  266. network = $1
  267. freqname = $2
  268. source = $3
  269. nickname = $4
  270. text = $5
  271. if not network =~ /^(QWalt)|(QDEV)/
  272. put_log "SYS Network name #{network} is unknown: QWalt or QDEV allowed."
  273. error = 1
  274. end
  275. if not freqname =~ /^(-qw-)|(-spam-)|(-dev-)/
  276. put_log "SYS Frequency name is unknown #{freqname}"
  277. error = 1
  278. end
  279. if not source =~ /^(#.+)|(qw:\/\/)|(http:\/\/)/
  280. put_log "SYS Source string is not in the form ^(#.+)|(qw:\/\/)|(http:\/\/) was: #{source}"
  281. error = 1
  282. end
  283. if not nickname =~ /^.+/
  284. put_log "SYS Nickname string is not in the form ^.+ #{nickname}"
  285. error = 1
  286. end
  287. if not text =~ /^.+/
  288. put_log "SYS Message string is not in the form ^.+ #{text}"
  289. error = 1
  290. end
  291. else # of check syntax
  292. put_log "SYS Command format is REQ_BC <network>,<frequency>,<source/channel>,'<nickname>','<message>'"
  293. error = 1
  294. end
  295. if error == 0
  296. payload = "%s,%s,%s,'%s','%s'" % [network, freqname, source, nickname, text]
  297. send_cmd(cmd, payload)
  298. # send REQ_BC to the corresponding role.
  299. end
  300. elsif cmd == "BC_RE"
  301. if inputmessage =~ /^(BC_RE) (.+) (.+)=(\d+),(.+)=(\d+)$/
  302. command = $1
  303. bcid = $2
  304. user_string = $3
  305. user_count = $4
  306. item_string = $5
  307. item_count = $6
  308. payload = "%s %s %s=%d,%s=%d" % [command, bcid, user_string, user_count, item_string, item_count]
  309. # according bcid it is possible to get the originating user.. so send only to his topic!
  310. if $broadcasts[bcid]
  311. to_user = $broadcasts[bcid]
  312. put_log "SYS Broadcast reply bcid: #{bcid} underway. To: '#{to_user}'."
  313. send("/topic/#{to_user}-replies", payload)
  314. #send_cmd(cmd, payload) .. and thus: central_brain has to collect the BC_REs
  315. else
  316. put_log "SYS Broadcast reply bcid: #{bcid} underway."
  317. send("/topic/broadcast", payload)
  318. #send_cmd(cmd, payload) .. and thus: central_brain has to collect the BC_REs
  319. end
  320. else
  321. put_log "SYS Format is BC_RE <bcid> <userstring>=<usercount>,<itemstring>=<itemcount>"
  322. end
  323. elsif cmd == "C"
  324. if payload =~ /^(.+)$/
  325. put_log "C #{$user}: #{$1}"
  326. send_cmd("C", "#{$1}")
  327. else
  328. put_log "SYS Format is C <chattext>"
  329. end
  330. elsif cmd == "WHO"
  331. put_log "SYS Asking who's here."
  332. send_cmd("WHO", "is here?")
  333. elsif cmd == "PART"
  334. put_log "SYS Goodbye '#{$user}'."
  335. send_cmd("PART", "The party was fun.")
  336. else
  337. # universal sender for other allowed commands.
  338. if sendmode && to_user
  339. put_debug "sendmode: #{sendmode} to_user: #{to_user}"
  340. send_cmd(cmd, payload, false, sendmode, to_user)
  341. else
  342. send_cmd(cmd, payload)
  343. end
  344. end # of if command == xyz
  345. else # of if allowed command
  346. put_log "SYS Command #{inputmessage} not allowed, your commands are: #{$my_cmds.join(", ")}."
  347. end # of if allowed command
  348. end # of if we should process it
  349. end
  350. # of while input != "part"
  351. end
  352. # of main
  353. main