Guest User

Untitled

a guest
Jan 1st, 2019
148
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.61 KB | None | 0 0
  1. =begin
  2.  
  3. SAlert by SpiffyJr
  4.  
  5. author: SpiffyJr
  6. name: SpiffyAlert
  7. tags: scripting, alert
  8.  
  9. changelog:
  10. 2018.03.14 - initial rewrite version
  11. =end
  12.  
  13. require 'xmpp4r'
  14.  
  15. if $SAFE > 0
  16. echo "error: This script needs to be trusted to work. (;trust #{script.name})"
  17. exit
  18. end
  19.  
  20. # <pushBold/>A <a exist="92672744" noun="brigand">giantman brigand</a><popBold/> slashes with a troll-claw at you!
  21.  
  22. class SAlert
  23. @@version = '2018.03.14'
  24. @@attack = '(?:(?:make a precise )?attempt|gesture|trace|aim|fire|swing|slash|charge|snaps at|thrust|spins a web|punch|weave|concentrate|channel|hurl|stomp|pound|bite|tries to ensare|claw)e?s?'
  25.  
  26. def SAlert.version
  27. @@version
  28. end
  29.  
  30. def initialize(settings)
  31. @attack_started = false
  32. @wait_for_prompt = false
  33. @respond_cmd = false
  34.  
  35. @buffer = []
  36. @last_send = Time.now.to_f
  37. @client = nil
  38. @settings = settings
  39. end
  40.  
  41. def remove_line(line)
  42. if (line === 'all')
  43. @settings[:lines] = {}
  44. return true
  45. end
  46.  
  47. line = self.clean_xml(line)
  48.  
  49. if not @settings[:lines][self.clean_xml(line)]
  50. return false
  51. end
  52.  
  53. @settings[:lines].delete(self.clean_xml(line))
  54. return true
  55. end
  56.  
  57. def handle_message(msg)
  58. echo("received message: #{msg.body}")
  59.  
  60. if msg.type == :error
  61. echo('error, invalid message received')
  62. elsif msg.body =~ /^,([^\s]+)(?:\s(.*))?/
  63. cmd = $1
  64. args = $2
  65.  
  66. if cmd =~ /clear/i
  67. res = self.remove_line(args)
  68. if args === 'all'
  69. self.message('cleared all lines')
  70. elsif res
  71. self.message('line cleared')
  72. else
  73. self.message('line not found')
  74. end
  75. elsif cmd =~ /save/i
  76. @settings[:lines_saved] = @settings[:lines].clone
  77. self.message('lines saved for future use')
  78. elsif cmd =~ /restore/i
  79. @settings[:lines] = @settings[:lines_saved].clone
  80. self.message('saved lines restored')
  81. elsif cmd =~ /town/i
  82. self.go2_town()
  83. elsif cmd =~ /chat/i
  84. self.message("sending \"chat #{args}\" to lnet")
  85. send_to_script('lnet', "chat #{args}")
  86. else
  87. self.message("unknown command: #{cmd}")
  88. end
  89. elsif msg.body !~ /^;/
  90. self.message("> #{msg.body}")
  91. clear()
  92. @respond_cmd = true
  93. put(msg.body)
  94. end
  95.  
  96. true
  97. end
  98.  
  99. def clean_xml(xml)
  100. # strip any "a" links first which should remove all the npcs, characters, random lewt, etc.
  101. xml.gsub!(/(?:some |an? )?<a exist="[^"]+" noun="[^"]+">[^<]+<\/a>/i, '')
  102.  
  103. # strip any xml characters
  104. xml.gsub!(/<[^>]+>/, '')
  105.  
  106. # lower-case only
  107. xml.downcase!
  108.  
  109. # replace some generic words
  110. xml.gsub!(/(?:north|east|south|west|up|down|out)/i, '')
  111.  
  112. # strip non-alpha chars
  113. xml.gsub!(/[^a-zA-Z]/, '')
  114.  
  115. xml.strip
  116. end
  117.  
  118. def disconnect()
  119. return if @settings[:debug]
  120.  
  121. if not @client
  122. return
  123. end
  124.  
  125. @client.close()
  126. @client = nil
  127.  
  128. echo('info: disconnected')
  129. end
  130.  
  131. def connect()
  132. return if @settings[:debug]
  133.  
  134. if @client
  135. return
  136. end
  137.  
  138. echo('info: connecting to remote server')
  139.  
  140. begin
  141. @client = Jabber::Client::new(Jabber::JID::new(@settings[:username]))
  142. @client.keepalive_interval = 30
  143. @client.connect
  144. @client.auth(@settings[:password])
  145. @client.send(Jabber::Presence.new.set_type(:available))
  146. rescue
  147. @client = nil
  148. echo("info: failed to connect: #{$!}")
  149. return
  150. end
  151.  
  152. # handle received messages
  153. @client.add_message_callback { |msg| self.handle_message(msg) }
  154.  
  155. echo('info: connected')
  156. end
  157.  
  158. def message(msg)
  159. if $frontend == 'stormfront'
  160. fam_window_begin = "<pushStream id=\"familiar\" ifClosedStyle=\"watching\"/>"
  161. fam_window_end = "<popStream/>\r\n"
  162. else
  163. fam_window_begin = "\034GSe\r\n"
  164. fam_window_end = "\034GSf\r\n"
  165. end
  166.  
  167. puts("#{fam_window_begin}#{msg}\r\n#{fam_window_end}")
  168.  
  169. return if @settings[:debug]
  170.  
  171. @buffer.push(msg)
  172. end
  173.  
  174. def process(input)
  175. xml = input[:xml].clone
  176. line = xml.gsub(/<[^>]+>/, '').strip
  177.  
  178. # skip if the line is empty after xml is stripped
  179. return if line.length === 0
  180.  
  181. # check player attack lines
  182. if @attack_started
  183. if xml =~ /Roundtime:? \d/i
  184. @attack_started = false
  185. end
  186. return
  187. elsif xml =~ /You #{@@attack}.*<pushBold\/>(?:The|An?) <a exist="[^"]+" noun="[^"]+">[^<]+<\/a><popBold\/>/i
  188. @attack_started = true
  189. return
  190. end
  191.  
  192. # some things require waiting on the next prompt line from the game
  193. if @wait_for_prompt
  194. if xml =~ /<prompt time="[^"]+">/
  195. @wait_for_prompt = false
  196. end
  197. return
  198. elsif xml =~ /flies out of the shadows toward/
  199. @wait_for_prompt = true
  200. return
  201. elsif xml =~ /You search (?:the|an?) <a exist="[^"]+" noun="[^"]+">[^<]+<\/a><popBold\/>/i
  202. # NPC searches
  203. @wait_for_prompt = true
  204. return
  205. elsif xml =~ /<pushBold\/>(?:The|An?) <a exist="[^"]+" noun="[^"]+">[^<]+<\/a><popBold\/> #{@@attack}/i
  206. # NPC attacks
  207. @wait_for_prompt = true
  208. return
  209. elsif xml =~ /<a exist="-\d+" noun="[^"]+">[^<]+<\/a> #{@@attack}.*<pushBold\/>(?:The|An?) <a exist="[^"]+" noun="[^"]+">[^<]+<\/a><popBold\/>/i
  210. # Player attacks
  211. @wait_for_prompt = true
  212. return
  213. end
  214.  
  215. # skip anything with inventory as an id
  216. if input[:inv].length > 0
  217. return if xml =~ /<a exist="#{input[:inv].reject { |i| i.id.nil? }.collect { |i| i.id }.join('|')}" noun="([^"]+)">([^<]+)<\/a>/
  218. end
  219.  
  220. # skip other GameObj based lines
  221. if xml =~ /<a exist="(-?\d+)" noun="([^"]+)">([^<]+)<\/a>/
  222. id = $1
  223. noun = $2
  224. name = $3
  225.  
  226. # check npcs now
  227. if not (obj = input[:npcs].find { |npc| npc.id == id })
  228. obj = GameObj.new(id, noun, name)
  229. end
  230.  
  231. # skip aggressive npcs, escorts, companions, passive npcs, etc.
  232. return if obj.type =~ /aggressive|escort|passive|familiar|companion/
  233.  
  234. # check if they're a pc and skip unless they're speaking
  235. if obj.id.to_i < 0 and obj.id.to_s.length === 9
  236. if xml =~ /<pushStream id="speech"/i
  237. # we alert here we just don't store it because it's speech
  238. self.message(line)
  239. end
  240.  
  241. return
  242. end
  243. end
  244.  
  245. # private messages from LNet get sent directly (alert, but don't store)
  246. if xml =~ /\[Private\]-(\w+):(\w+): "([^"]+)"/
  247. self.message("[Private]-#{$1}:#{$2}: \"#{$3}\"")
  248. return
  249. end
  250.  
  251. # skip the Zul Logoth carts
  252. if input[:room] == 19547
  253. return
  254. end
  255.  
  256. # skip certain rooms
  257. if Room[input[:room]]
  258. room = Room[input[:room]]
  259.  
  260. # Voln rooms
  261. return if room.title.include?('[Path to Enlightenment]')
  262.  
  263. # Room tags that match
  264. return if room.tags.any? { |t| @settings[:ignore_tags].include?(t) }
  265. end
  266.  
  267. # skip when certain scripts are running
  268. return if input[:scripts].any? { |s| @settings[:ignore_scripts].include?(s) }
  269.  
  270. # skip prompts (don't move me around - stuff above checks for prompts to know when to stop)
  271. return if xml =~ /<prompt time="\d+">[^<]+<\/prompt>/
  272.  
  273. # skip room titles and descriptions
  274. return if xml =~ /(?:<style id="roomDesc|roomName" \/>|<compDef id='room desc|room objs'>)/
  275.  
  276. # skip pushStreams
  277. return if xml =~ /<pushStream/i
  278.  
  279. # skip our own talk lines
  280. return if xml =~ /<preset id='speech'>[^<]+<a exist="[^"]+" coord="[^"]+" noun="help">[^<]+<\/a>/
  281.  
  282. if (cleaned = self.clean_xml(xml.clone)).length == 0
  283. return
  284. end
  285.  
  286. if not @settings[:lines][cleaned]
  287. self.message(line)
  288.  
  289. @settings[:lines][cleaned] = true
  290. end
  291. end
  292.  
  293. def send()
  294. if @buffer.empty? or Time.now.to_f - @last_send < 1.0
  295. return
  296. end
  297.  
  298. # take the next 25 lines and send at once
  299. line = @buffer.shift(25).join("\n")
  300.  
  301. msg = Jabber::Message::new(@settings[:receiver], line)
  302. msg.type = :chat
  303.  
  304. if not @client
  305. self.connect()
  306. end
  307.  
  308. begin
  309. @client.send(msg)
  310. rescue
  311. echo("info: failed to send: #{$!}")
  312. echo("info: retrying once")
  313.  
  314. self.disconnect()
  315. self.connect()
  316.  
  317. @client.send(msg)
  318. end
  319.  
  320. @last_send = Time.now.to_f
  321. end
  322.  
  323. def run()
  324. loop do
  325. xml = get?
  326.  
  327. self.send()
  328.  
  329. if xml
  330. if @respond_cmd
  331. if xml =~ /<prompt time="\d+">[^<]+<\/prompt>/
  332. @respond_cmd = false
  333. clear()
  334. else
  335. self.message(xml.gsub(/<[^>]+>/, '').strip)
  336. end
  337. else
  338. self.process({
  339. :inv => [GameObj.left_hand] + [GameObj.right_hand] + GameObj.inv.to_a.clone,
  340. :npcs => GameObj.npcs.to_a,
  341. :pcs => GameObj.pcs.to_a,
  342. :room => Room.current.id,
  343. :scripts => Script.running.collect { |s| s.name },
  344. :xml => xml,
  345. })
  346. end
  347. end
  348.  
  349. sleep(0.10)
  350. end
  351. end
  352. end
  353.  
  354. default_settings = {
  355. :lines => [{}, ''],
  356. :lines_saved => [{}, ''],
  357.  
  358. :debug => [false, 'enable debug mode (no sending messages)'],
  359. :ignore_scripts => [['sloot', 'slootbeta', 'useherbs'], 'scripts to ignore'],
  360. :ignore_tags => [['town', 'consignment', 'pawnshop', 'locksmith', 'gemshop', 'herbalist', 'bank', 'furrier', 'chronomage', 'advguard', 'advguild', 'advguild2'], 'room tags to ignore'],
  361.  
  362. :password => ['', 'password to login to XMPP with'],
  363. :receiver => ['', 'who to send alerts to'],
  364. :username => ['', 'username to login to XMPP with'],
  365. }
  366.  
  367. settings = CharSettings.to_hash
  368. default_settings.each do |k, v|
  369. if settings[k].nil?
  370. settings[k] = v[0]
  371. end
  372. end
  373.  
  374. settings.delete_if { |setting, value| default_settings[setting].nil? }
  375.  
  376. if script.vars[1] == 'help'
  377. print_option = proc do |option, msg, eg = "", pad = 2|
  378. if eg != ""
  379. msg = sprintf("%-40s #{$lich_char}#{script.name} #{eg}", msg)
  380. end
  381.  
  382. respond(sprintf(" %s%-25s %s", " " * pad, option, msg))
  383. end
  384.  
  385. respond("SAlert #{SAlert.version} by SpiffyJr (Kips)")
  386. respond("")
  387. respond("SAlert is a script to alert you to messages you haven't seen before.")
  388. respond("")
  389. respond("Basic usage: #{$lich_char}#{script.name}")
  390. respond("")
  391. respond("Extra commands:")
  392. print_option.call("help", "show this help message")
  393. print_option.call("list", "list current settings")
  394. elsif script.vars[1] == 'list'
  395. respond('======================')
  396. respond('SAlert settings')
  397. respond('======================')
  398. settings.sort.each do |k, v|
  399. if k == :lines or k == :lines_saved
  400. # skip showing lines
  401. elsif v.class == Array
  402. puts("#{monsterbold_start}#{k}#{monsterbold_end}: #{v.sort.join(', ')}")
  403. else
  404. puts("#{monsterbold_start}#{k}#{monsterbold_end}: #{v}")
  405. end
  406. end
  407. elsif script.vars[0] =~ /^([^\s]+)(.*)?$/ and not settings[$1.downcase.strip.to_sym].nil?
  408. setting = $1.downcase.to_sym
  409. value = $2.strip.downcase
  410. action = nil
  411.  
  412. if not defined?(settings[setting]) or settings[setting].nil?
  413. echo "** setting \"#{setting}\" does not exist"
  414. exit
  415. end
  416.  
  417. if value =~ /^([+-])(.*)$/
  418. action = $1
  419. value = $2.strip.downcase
  420. end
  421.  
  422. if settings[setting].class == Array
  423. settings[setting].delete(value)
  424. if action == '-'
  425. echo "-- \"#{value}\" removed from \"#{setting}\""
  426. else
  427. settings[setting].push(value)
  428. echo "-- \"#{value}\" added to \"#{setting}\""
  429. end
  430.  
  431. echo "-- \"#{setting}\" is now \"#{settings[setting].sort.join(', ')}\""
  432. else
  433. if settings[setting].class == FalseClass or settings[setting].class == TrueClass
  434. value = (value =~ /^true|1|yes|on/) ? true : false
  435. elsif settings[setting].class == Fixnum
  436. value = value.to_i
  437. end
  438.  
  439. echo "-- set \"#{setting}\" to \"#{value}\""
  440. settings[setting] = value
  441. end
  442. elsif script.vars[1] == 'clear'
  443. $salert = SAlert.new(settings)
  444. res = $salert.remove_line(script.vars[2])
  445. if script.vars[2] === 'all'
  446. echo '-- cleared all lines'
  447. elsif res
  448. echo '-- line cleared'
  449. else
  450. echo '-- line not found'
  451. end
  452. elsif script.vars[1] == 'update'
  453. Script.run('repository', 'download salert')
  454. echo '-- updated'
  455. elsif not script.vars[1].nil?
  456. echo "** You're doing it wrong! Try #{$lich_char}#{script.name} help"
  457. else
  458. script.want_downstream = false
  459. script.want_downstream_xml = true
  460.  
  461. $salert = SAlert.new(settings)
  462. $salert.connect()
  463.  
  464. Thread.new do
  465. loop do
  466. wait_until { bleeding? }
  467. $salert.message('You are bleeding!')
  468. wait_while { bleeding? }
  469. $salert.message('You are no longer bleeding!')
  470. end
  471. end
  472.  
  473. Thread.new do
  474. loop do
  475. if dead?
  476. $salert.message('You just died! Exiting game in 5 seconds...')
  477. sleep(5)
  478. fput "quit"
  479. end
  480.  
  481. if not $favor.nil?
  482. favor_to_step = (Society.rank*100)+(((XMLData.level**2)*(((Society.rank+2)/3)*5))/3)
  483. favor_percent = $favor[:step]/favor_to_step.to_f*100
  484.  
  485. if favor_percent > 100
  486. $salert.message('You are ready for your next voln step')
  487. wait_while { $favor[:step]/favor_to_step.to_f*100 > 100 }
  488. end
  489. end
  490.  
  491. sleep(1)
  492. end
  493. end
  494.  
  495. $salert.run()
  496. end
Add Comment
Please, Sign In to add comment