Module: Lich::Util

Includes:
Enumerable
Defined in:
lib/update.rb,
lib/util/util.rb,
lib/magic-info.rb

Defined Under Namespace

Modules: Magicinfo, Update

Class Method Summary collapse

Class Method Details

.anon_hook(prefix = '') ⇒ String

Generates an anonymous hook name based on the current time and a random number.

Examples:

Lich::Util.anon_hook("test")

Parameters:

  • prefix (String) (defaults to: '')

    An optional prefix for the hook name.

Returns:

  • (String)

    The generated anonymous hook name.



85
86
87
88
# File 'lib/util/util.rb', line 85

def self.anon_hook(prefix = '')
  now = Time.now
  "Util::#{prefix}-#{now}-#{Random.rand(10000)}"
end

.install_gem_requirements(gems_to_install) ⇒ Object

Installs the specified gems and requires them if specified.

Examples:

Lich::Util.install_gem_requirements({"some_gem" => true})

Parameters:

  • gems_to_install (Hash)

    A hash where keys are gem names and values are booleans indicating whether to require them.

Raises:

  • (ArgumentError)

    If the input is not a hash or if the hash does not contain valid key-value pairs.

  • (RuntimeError)

    If any gem installation fails.



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/util/util.rb', line 263

def self.install_gem_requirements(gems_to_install)
  raise ArgumentError, "install_gem_requirements must be passed a Hash" unless gems_to_install.is_a?(Hash)
  require "rubygems"
  require "rubygems/dependency_installer"
  installer = Gem::DependencyInstaller.new({ :user_install => true, :document => nil })
  installed_gems = Gem::Specification.map { |gem| gem.name }.sort.uniq
  failed_gems = []

  gems_to_install.each do |gem_name, should_require|
    unless gem_name.is_a?(String) && (should_require.is_a?(TrueClass) || should_require.is_a?(FalseClass))
      raise ArgumentError, "install_gem_requirements must be passed a Hash with String key and TrueClass/FalseClass as value"
    end
    begin
      unless installed_gems.include?(gem_name)
        respond("--- Lich: Installing missing ruby gem '#{gem_name}' now, please wait!")
        installer.install(gem_name)
        respond("--- Lich: Done installing '#{gem_name}' gem!")
      end
      require gem_name if should_require
    rescue StandardError
      respond("--- Lich: error: Failed to install Ruby gem: #{gem_name}")
      respond("--- Lich: error: #{$!}")
      Lich.log("error: Failed to install Ruby gem: #{gem_name}")
      Lich.log("error: #{$!}")
      failed_gems.push(gem_name)
    end
  end
  unless failed_gems.empty?
    raise("Please install the failed gems: #{failed_gems.join(', ')} to run #{$lich_char}#{Script.current.name}")
  end
end

.issue_command(command, start_pattern, end_pattern = /<prompt/, include_end: true, timeout: 5, silent: nil, usexml: true, quiet: false, use_fput: true) ⇒ Array<String>

Issues a command and captures the output between start and end patterns.

Examples:

output = Lich::Util.issue_command("look", /You see/, /<prompt/)

Parameters:

  • command (String)

    The command to execute.

  • start_pattern (Regexp)

    The pattern indicating the start of the output to capture.

  • end_pattern (Regexp) (defaults to: /<prompt/)

    The pattern indicating the end of the output to capture (default: /<prompt/).

  • include_end (Boolean) (defaults to: true)

    Whether to include the end line in the result (default: true).

  • timeout (Integer) (defaults to: 5)

    The maximum time to wait for the command to complete (default: 5).

  • silent (Boolean, nil) (defaults to: nil)

    Whether to suppress output (default: nil).

  • usexml (Boolean) (defaults to: true)

    Whether to use XML output (default: true).

  • quiet (Boolean) (defaults to: false)

    Whether to suppress output during capture (default: false).

  • use_fput (Boolean) (defaults to: true)

    Whether to use fput for command execution (default: true).

Returns:

  • (Array<String>)

    The captured output lines.

Raises:

  • (Timeout::Error)

    If the command times out.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/util/util.rb', line 106

def self.issue_command(command, start_pattern, end_pattern = /<prompt/, include_end: true, timeout: 5, silent: nil, usexml: true, quiet: false, use_fput: true)
  result = []
  name = self.anon_hook
  filter = false

  save_script_silent = Script.current.silent
  save_want_downstream = Script.current.want_downstream
  save_want_downstream_xml = Script.current.want_downstream_xml

  Script.current.silent = silent if !silent.nil?
  Script.current.want_downstream = !usexml
  Script.current.want_downstream_xml = usexml

  begin
    Timeout::timeout(timeout, Interrupt) {
      DownstreamHook.add(name, proc { |line|
        if filter
          if line =~ end_pattern
            DownstreamHook.remove(name)
            filter = false
            if quiet
              next(nil)
            else
              line
            end
          else
            if quiet
              next(nil)
            else
              line
            end
          end
        elsif line =~ start_pattern
          filter = true
          if quiet
            next(nil)
          else
            line
          end
        else
          line
        end
      })
      use_fput ? fput(command) : put(command)

      until (line = get) =~ start_pattern; end
      result << line.rstrip
      until (line = get) =~ end_pattern
        result << line.rstrip
      end
      if include_end
        result << line.rstrip
      end
    }
  rescue Interrupt
    nil
  ensure
    DownstreamHook.remove(name)
    Script.current.silent = save_script_silent if !silent.nil?
    Script.current.want_downstream = save_want_downstream
    Script.current.want_downstream_xml = save_want_downstream_xml
  end
  return result
end

.normalize_lookup(effect, val) ⇒ Boolean

Normalizes the lookup for effects based on the provided value.

Examples:

Lich::Util.normalize_lookup('some_effect', 'some_value')

Parameters:

  • effect (String)

    The effect type to look up.

  • val (String, Integer, Symbol)

    The value to normalize and check.

Returns:

  • (Boolean)

    True if the normalized value exists, false otherwise.

Raises:

  • (RuntimeError)

    If an invalid lookup case is provided.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/util/util.rb', line 40

def self.normalize_lookup(effect, val)
  caller_type = "Effects::#{effect}"
  case val
  when String
    (eval caller_type).to_h.transform_keys(&:to_s).transform_keys(&:downcase).include?(val.downcase.gsub('_', ' '))
  when Integer
    #      seek = mappings.fetch(val, nil)
    (eval caller_type).active?(val)
  when Symbol
    (eval caller_type).to_h.transform_keys(&:to_s).transform_keys(&:downcase).include?(val.to_s.downcase.gsub('_', ' '))
  else
    fail "invalid lookup case #{val.class.name}"
  end
end

.normalize_name(name) ⇒ String

Normalizes a name by converting it to a standard format.

Examples:

Lich::Util.normalize_name("vault-kick")
# => "vault_kick"

Parameters:

  • name (String, Symbol)

    The name to normalize.

Returns:

  • (String)

    The normalized name.



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/util/util.rb', line 63

def self.normalize_name(name)
  # there are five cases to normalize
  # "vault_kick", "vault kick", "vault-kick", :vault_kick, :vaultkick
  # "predator's eye"
  # if present, convert spaces to underscore; convert all to downcase string
  normal_name = name.to_s.downcase
  normal_name.gsub!(' ', '_') if name =~ (/\s/)
  normal_name.gsub!('-', '_') if name =~ (/-/)
  normal_name.gsub!(":", '') if name =~ (/:/)
  normal_name.gsub!("'", '') if name =~ (/'/)
  normal_name
end

.quiet_command(command, start_pattern, end_pattern, include_end = true, timeout = 5, silent = true) ⇒ Array<String>

Issues a quiet command and captures the output.

Examples:

output = Lich::Util.quiet_command("look", /You see/, /<prompt/)

Parameters:

  • command (String)

    The command to execute.

  • start_pattern (Regexp)

    The pattern indicating the start of the output to capture.

  • end_pattern (Regexp)

    The pattern indicating the end of the output to capture.

  • include_end (Boolean) (defaults to: true)

    Whether to include the end line in the result (default: true).

  • timeout (Integer) (defaults to: 5)

    The maximum time to wait for the command to complete (default: 5).

  • silent (Boolean) (defaults to: true)

    Whether to suppress output (default: true).

Returns:

  • (Array<String>)

    The captured output lines.



199
200
201
# File 'lib/util/util.rb', line 199

def self.quiet_command(command, start_pattern, end_pattern, include_end = true, timeout = 5, silent = true)
  return issue_command(command, start_pattern, end_pattern, include_end: include_end, timeout: timeout, silent: silent, usexml: false, quiet: true)
end

.quiet_command_xml(command, start_pattern, end_pattern = /<prompt/, include_end = true, timeout = 5, silent = true) ⇒ Array<String>

Issues a quiet command and captures the output in XML format.

Examples:

output = Lich::Util.quiet_command_xml("look", /You see/, /<prompt/)

Parameters:

  • command (String)

    The command to execute.

  • start_pattern (Regexp)

    The pattern indicating the start of the output to capture.

  • end_pattern (Regexp) (defaults to: /<prompt/)

    The pattern indicating the end of the output to capture (default: /<prompt/).

  • include_end (Boolean) (defaults to: true)

    Whether to include the end line in the result (default: true).

  • timeout (Integer) (defaults to: 5)

    The maximum time to wait for the command to complete (default: 5).

  • silent (Boolean) (defaults to: true)

    Whether to suppress output (default: true).

Returns:

  • (Array<String>)

    The captured output lines.



183
184
185
# File 'lib/util/util.rb', line 183

def self.quiet_command_xml(command, start_pattern, end_pattern = /<prompt/, include_end = true, timeout = 5, silent = true)
  return issue_command(command, start_pattern, end_pattern, include_end: include_end, timeout: timeout, silent: silent, usexml: true, quiet: true)
end

.silver_count(timeout = 3) ⇒ Integer

Counts the amount of silver and returns it as an integer.

Examples:

silver_amount = Lich::Util.silver_count

Parameters:

  • timeout (Integer) (defaults to: 3)

    The maximum time to wait for the count (default: 3).

Returns:

  • (Integer)

    The amount of silver counted.

Raises:

  • (RuntimeError)

    If the counting process fails.



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/util/util.rb', line 211

def self.silver_count(timeout = 3)
  silence_me unless (undo_silence = silence_me)
  result = ''
  name = self.anon_hook
  filter = false

  start_pattern = /^\s*Name\:/
  end_pattern = /^\s*Mana\:\s+\-?[0-9]+\s+Silver\:\s+([0-9,]+)/
  ttl = Time.now + timeout
  begin
    # main thread
    DownstreamHook.add(name, proc { |line|
      if filter
        if line =~ end_pattern
          result = $1.dup
          DownstreamHook.remove(name)
          filter = false
        else
          next(nil)
        end
      elsif line =~ start_pattern
        filter = true
        next(nil)
      else
        line
      end
    })
    # script thread
    fput 'info'
    loop {
      # non-blocking check, this allows us to
      # check the time even when the buffer is empty
      line = get?
      break if line && line =~ end_pattern
      break if Time.now > ttl
      sleep(0.01) # prevent a tight-loop
    }
  ensure
    DownstreamHook.remove(name)
    silence_me if undo_silence
  end
  return result.gsub(',', '').to_i
end