class SecretKnock

secret_knock.rb

Attributes

h[R]
listening[R]
message[R]

Public Class Methods

new(verbose: true, external: nil, short_delay: 0.35, long_delay: 0.9, debug: false) click to toggle source
# File lib/secret_knock.rb, line 10
def initialize(verbose: true, external: nil, 
               short_delay: 0.35, long_delay: 0.9, debug: false)

  @verbose, @external, @debug = verbose, external, debug

  # ranked by frequency. see https://en.wikipedia.org/wiki/Letter_frequency
  keys = ["e", "t", "a", "o", "i", "n", "s", "h", "r", "d", "l", "c", "u", "m",
                      "w", "f", "g", "y", "p", "b", "v", "k", "j", "x", "q", "z"]


  # number the keys including numbers and special functions (i.e. backspace)

  i = 0
  a = ([' '] + keys + ('1'..'9').to_a + ['0','<backspace>']).map do |x|
    i += 1
    i += 1 while i.to_s[-1].to_i > 6 or i.to_s[-1].to_i == 0
    [x, i]
  end

  # create and sort in alphabetical order the
  #   hash lookup and inverted hash lookup

  @h = (a.take(27).sort_by(&:first) + a[27..-1]).to_h
  @h.merge!({"7" => 7, "8" => 8, "9" => 9})

  #>  h contains the following
  # {
  #    " "=>1, "a"=>4, "b"=>33, "c"=>21, "d"=>15, "e"=>2, "f"=>25, "g"=>26,
  #    "h"=>13, "i"=>6, "j"=>36, "k"=>35, "l"=>16, "m"=>23, "n"=>11, "o"=>5,
  #    "p"=>32, "q"=>42, "r"=>14, "s"=>12, "t"=>3, "u"=>22, "v"=>34, "w"=>24,
  #    "x"=>41, "y"=>31, "z"=>43, "1"=>44, "2"=>45, "3"=>46, "4"=>51, "5"=>52,
  #    "6"=>53, "7"=>7, "8"=>8, "9"=>9, "0"=>61, "<backspace>"=>62
  # }



  @hb = h.invert

  @short_delay, @long_delay = short_delay, long_delay # seconds
  @listening = false

end

Public Instance Methods

autoknock(s) click to toggle source
# File lib/secret_knock.rb, line 53
def autoknock(s)
  playback knockerize(s)
end
detect(timeout: 2, repeat: true) click to toggle source

listens for knocking in the background which allows the control

# File lib/secret_knock.rb, line 58
def detect(timeout: 2, repeat: true)

  listen
  
  Thread.new do
    
    if @external and @external.respond_to? :on_listening then
      @external.on_listening
    end
    
    sleep 0.2 while @t + timeout > Time.now or @a.length <= 1
    
    if @external and @external.respond_to? :on_processing then
      @external.on_processing
    end
    
    msg = decipher()
    on_timeout msg
    
    begin
      @external.message msg if @external
    rescue
      puts
      puts "\rSecretKnock @external notifier " +  ($!).inspect
    end

    detect(timeout: timeout, repeat: repeat) if repeat
  end
end
knock()
Alias for: knocked
knocked() click to toggle source
# File lib/secret_knock.rb, line 88
def knocked()

  t1 = Time.now

  if t1 < @t + @short_delay then

    @i += 1

  else

    @a << ',' if t1 > @t + @long_delay
    puts '' if @verbose
    @i = 1
    @a << 0

  end
  
  print @i.to_s + ' ' if @verbose

  @a[-1] = @i if @a.any?
  
  @t = Time.now

  @external.knock if @external
  
  puts '@a: ' + @a.inspect if @debug
  
end
Also aliased as: knock
knockerize(s) click to toggle source
# File lib/secret_knock.rb, line 126
def knockerize(s)

  s.chars.flat_map do |x| 
    @h[x].to_s.chars.map {|n| [:knocks, n.to_i]}  + [:pause]
  end

end
knocks(n) click to toggle source
# File lib/secret_knock.rb, line 119
def knocks(n)

  n.times { knocked }
  short_pause

end
listen() { |self| ... } click to toggle source
# File lib/secret_knock.rb, line 134
def listen()

  @t = Time.now
  @i = 1
  @a = []    
  
  @listening = true

  if block_given? then
    yield self
    stop_listening
  end

end
long_pause() click to toggle source
# File lib/secret_knock.rb, line 149
def long_pause()

  puts "\n*pause*\n\n" if @verbose
  sleep @long_delay + 0.1    

end
Also aliased as: pause
on_timeout(msg='') click to toggle source
# File lib/secret_knock.rb, line 156
def on_timeout(msg='')
  # override this method if you wish
end
pause()
Alias for: long_pause
playback(instructions) click to toggle source
# File lib/secret_knock.rb, line 162
def playback(instructions)

  listen do |x|

    sleep 1

    instructions.each do |action|
      name, args = action
      args ? x.method(name).call(args) : x.method(name).call
    end
    
  end    

end
reset() click to toggle source
# File lib/secret_knock.rb, line 177
def reset()
  @a = []
end
short_pause() click to toggle source
# File lib/secret_knock.rb, line 181
def short_pause()
  sleep @short_delay + 0.1
end
stop_listening() click to toggle source
# File lib/secret_knock.rb, line 185
def stop_listening()

  @listening = false
  @message = decipher()

end

Private Instance Methods

decipher() click to toggle source
# File lib/secret_knock.rb, line 194
def decipher()
  
  @a.join.gsub(/(\d{2})(\d)/,'\1,\2').split(',').inject([]) do |r, x|
    (x != '42' ? r << @hb[x.to_i] : r[0..-2])
  end.join    

end