From cbe7aead32ea241b69870625fb6ec70fb32e145e Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Mon, 7 Aug 2017 00:25:01 -0500 Subject: [PATCH] Add keymap replacement strategy Adds a replacement strategy which uses knowledge of a particular keyboard layout to create typos which would be easy mistakes on the configured layout. Seems to be pivoting off the wrong character in the source text right now, but is otherwise working. --- data/keymaps/en-US_qwerty.yml | 146 ++++++++++++++++++++ src/typhar.cr | 1 + src/typhar/cli.cr | 3 + src/typhar/exceptions.cr | 3 + src/typhar/strategies/replacement/keymap.cr | 30 ++++ src/typhar/transformer.cr | 1 + src/typhar/types/keymap.cr | 39 ++++++ src/typhar/types/keymap_entry.cr | 10 ++ 8 files changed, 233 insertions(+) create mode 100644 data/keymaps/en-US_qwerty.yml create mode 100644 src/typhar/strategies/replacement/keymap.cr create mode 100644 src/typhar/types/keymap.cr create mode 100644 src/typhar/types/keymap_entry.cr diff --git a/data/keymaps/en-US_qwerty.yml b/data/keymaps/en-US_qwerty.yml new file mode 100644 index 0000000..41213ec --- /dev/null +++ b/data/keymaps/en-US_qwerty.yml @@ -0,0 +1,146 @@ +--- +data: + '`': + shift: '~' + neighbors: ['1'] + '1': + shift: '!' + neighbors: ['`', 'q'] + '2': + shift: '@' + neighbors: ['1', 'q', '3'] + '3': + shift: '#' + neighbors: ['2', 'w', 'e', '4'] + '4': + shift: '$' + neighbors: ['3', 'e', 'r', '5'] + '5': + shift: '%' + neighbors: ['4', 'r', 't', '6'] + '6': + shift: '^' + neighbors: ['5', 't', 'y', '7'] + '7': + shift: '&' + neighbors: ['6', 'y', 'u', '8'] + '8': + shift: '*' + neighbors: ['7', 'u', 'i', '9'] + '9': + shift: '(' + neighbors: ['8', 'i', 'o', '0'] + '0': + shift: ')' + neighbors: ['9', 'o', 'p', '-'] + '-': + shift: '_' + neighbors: ['0', 'p', '[', '='] + '=': + shift: '+' + neighbors: ['-', '[', ']'] + q: + shift: 'Q' + neighbors: ['1', '2', 'w', 'a'] + w: + shift: 'W' + neighbors: ['q', '2', '3', 'e', 'a', 's'] + e: + shift: 'E' + neighbors: ['w', '3', '4', 'r', 'd', 's'] + r: + shift: 'R' + neighbors: ['e', '4', '5', 't', 'f', 'd'] + t: + shift: 'T' + neighbors: ['r', '5', '6', 'y', 'g', 'f'] + y: + shift: 'Y' + neighbors: ['t', '6', '7', 'u', 'h', 'g'] + u: + shift: 'U' + neighbors: ['y', '7', '8', 'i', 'j', 'h'] + i: + shift: 'I' + neighbors: ['u', '8', '9', 'o', 'k', 'j'] + o: + shift: 'O' + neighbors: ['i', '9', '0', 'p', 'l', 'k'] + p: + shift: 'P' + neighbors: ['o', '0', '-', '[', ';', 'l'] + '[': + shift: '{' + neighbors: ['p', '-', '=', ']', "'", ';'] + ']': + shift: '}' + neighbors: ['[', '=', '\', "'"] + '\': + shift: '|' + neighbors: [']'] + a: + shift: 'A' + neighbors: ['q', 'w', 's', 'z'] + s: + shift: 'S' + neighbors: ['a', 'w', 'e', 'd', 'x', 'z'] + d: + shift: 'D' + neighbors: ['s', 'e', 'r', 'f', 'c', 'x'] + f: + shift: 'F' + neighbors: ['d', 'r', 't', 'g', 'v', 'c'] + g: + shift: 'G' + neighbors: ['f', 't', 'y', 'h', 'b', 'v'] + h: + shift: 'H' + neighbors: ['g', 'y', 'u', 'j', 'n', 'b'] + j: + shift: 'J' + neighbors: ['h', 'u', 'i', 'k', 'm', 'n'] + k: + shift: 'K' + neighbors: ['j', 'i', 'o', 'l', ',', 'm'] + l: + shift: 'L' + neighbors: ['k', 'o', 'p', ';', '.', ','] + ';': + shift: ':' + neighbors: ['l', 'p', '[', "'", '/', '.'] + "'": + shift: '"' + neighbors: [';', '[', ']', '/'] + z: + shift: 'Z' + neighbors: ['a', 's', 'x'] + x: + shift: 'X' + neighbors: ['z', 's', 'd', 'c'] + c: + shift: 'C' + neighbors: ['x', 'd', 'f', 'v'] + v: + shift: 'V' + neighbors: ['c', 'f', 'g', 'b'] + b: + shift: 'B' + neighbors: ['v', 'g', 'h', 'n'] + n: + shift: 'N' + neighbors: ['b', 'h', 'j', 'm'] + m: + shift: 'M' + neighbors: ['n', 'j', 'k', ','] + ',': + shift: '<' + neighbors: ['m', 'k', 'l', '.'] + '.': + shift: '>' + neighbors: [',', 'l', ';', '/'] + '/': + shift: '?' + neighbors: ['.', ';', "'"] + ' ': + shift: ' ' + neighbors: ['x', 'c', 'v', 'b', 'n', 'm', ','] diff --git a/src/typhar.cr b/src/typhar.cr index 1749e37..c80c78d 100644 --- a/src/typhar.cr +++ b/src/typhar.cr @@ -1,5 +1,6 @@ require "cli" require "secure_random" +require "yaml" require "./typhar/*" module Typhar diff --git a/src/typhar/cli.cr b/src/typhar/cli.cr index 2f811d3..a51acb0 100644 --- a/src/typhar/cli.cr +++ b/src/typhar/cli.cr @@ -83,6 +83,9 @@ module Typhar when "n-shifter" codepoint_shift = options.codepoint_shift.to_i Typhar::ReplacementStrategies::NShifter.new(seed, codepoint_shift) + when "keymap" + keymap_name = "en-US_qwerty" + Typhar::ReplacementStrategies::Keymap.new(seed, keymap_name) else raise StrategyDoesNotExistException.new("'#{strategy}' does not exist.") end diff --git a/src/typhar/exceptions.cr b/src/typhar/exceptions.cr index a1ec217..876499a 100644 --- a/src/typhar/exceptions.cr +++ b/src/typhar/exceptions.cr @@ -4,4 +4,7 @@ module Typhar class StrategyDoesNotExistException < Exception end + + class UnknownKeyError < Exception + end end \ No newline at end of file diff --git a/src/typhar/strategies/replacement/keymap.cr b/src/typhar/strategies/replacement/keymap.cr new file mode 100644 index 0000000..d45b152 --- /dev/null +++ b/src/typhar/strategies/replacement/keymap.cr @@ -0,0 +1,30 @@ +module Typhar + module ReplacementStrategies + class Keymap < Base + @keymap : Typhar::Types::Keymap + + getter keymap_name + getter keymap + + def initialize(@seed : UInt32, @keymap_name : String) + keymap_yml = File.read("./data/keymaps/#{keymap_name}.yml") + @keymap = Typhar::Types::Keymap.from_yaml(keymap_yml).not_nil! + @keymap.dereference! + end + + def replace(to_replace : String | Char) : String | Char + keymap_entry = keymap[to_replace]? + + if keymap_entry + keymap_entry.neighbors.sample(sampler) + else + raise UnknownKeyError.new("Unknown key '#{to_replace}' in keymap") + end + end + + private def sampler + @sampler ||= Random.new(seed) + end + end + end +end diff --git a/src/typhar/transformer.cr b/src/typhar/transformer.cr index f5602b2..3602ba8 100644 --- a/src/typhar/transformer.cr +++ b/src/typhar/transformer.cr @@ -1,3 +1,4 @@ +require "./types/*" require "./strategies/*" module Typhar diff --git a/src/typhar/types/keymap.cr b/src/typhar/types/keymap.cr new file mode 100644 index 0000000..226329c --- /dev/null +++ b/src/typhar/types/keymap.cr @@ -0,0 +1,39 @@ +module Typhar + module Types + class Keymap + YAML.mapping( + data: { + type: Hash(String, KeymapEntry) + } + ) + + def [](key) + data[key]?.not_nil! + end + + def []?(key) + data.fetch(key.to_s) do |key| + result = data.find { |k, v| v.shift == key } + if result + result[1] + else + nil + end + end + end + + def dereference! + data.map do |key, value| + value.neighbors = value.neighbors.flat_map do |neighbor| + if data[neighbor]? + neighbor_entry = data[neighbor] + [neighbor, neighbor_entry.shift] + else + raise UnknownKeyError.new("Unknown key '#{neighbor}' in keymap") + end + end + end + end + end + end +end diff --git a/src/typhar/types/keymap_entry.cr b/src/typhar/types/keymap_entry.cr new file mode 100644 index 0000000..bc66b32 --- /dev/null +++ b/src/typhar/types/keymap_entry.cr @@ -0,0 +1,10 @@ +module Typhar + module Types + class KeymapEntry + YAML.mapping( + shift: String, + neighbors: Array(String) + ) + end + end +end