connection.rb 12 KB

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