keystroke-transcriber 1.0.1
Records keypress events and converts them for replay on programmable USB HID devices
License: Apache 2.0

keystroke_transcriber: records your keystrokes and writes an Arduino sketch to replay the same keystrokes
---------------------------------------------------------------------------------------------------------

If you need to write arduino sketches for USB HID keyboard emulation (Digispark, Teensy, Ducky USB), then
this module might be useful to you.

This module records global keystroke events on your PC (when you tell it to),
and uses the recorded keystroke sequence to write the sketch for you.

Simply tell it which programmable USB HID keyboard platform you're using, and start
pressing keys. keystroke_transcriber will generate a sketch file for you. You can
even choose to maintain the timing between your original keypresses, if you want.

Limitations
-----------

Currently only supports Digispark: Support for Ducky USB and Teensy devices will be coming soon

Install
-------

Install from pypi:

::

    pip install keystroke_transcriber

keystroke_transcriber command-line arguments
--------------------------------------------

::

    usage: keystroke_transcriber [-h] [-p {oneshot,repeat-forever,repeat-n}]
                                 [-t {digispark}] [-o OUTPUT_FILE]
                                 [-n REPEAT_COUNT] [-D REPEAT_DELAY_MS]
                                 [-d EVENT_DELAY_MS] [-m] [-r RECORD_SECONDS] [-s]
                                 [-q]

    Records global keypress events until Ctrl-C is pressed (or until a fixed time
    has elapsed), and translates them into a program that replays those keypress
    events on some programmable USB HID device (e.g. Digispark)

    optional arguments:
      -h, --help            show this help message and exit
      -p {oneshot,repeat-forever,repeat-n}, --playback-type {oneshot,repeat-forever,repeat-n}
                            Set the playback style for recorded keystroke
                            sequences (default: oneshot)
      -t {digispark}, --target-type {digispark}
                            Set the type of programmable USB HID device to
                            generate output for (default: digispark)
      -o OUTPUT_FILE, --output-file OUTPUT_FILE
                            Write output to this file, instead of printing output
                            to the terminal (default: None)
      -n REPEAT_COUNT, --repeat-count REPEAT_COUNT
                            Sets how many times the recorded keystroke sequence
                            should be repeated (only used if --playback-type is
                            repeat-n) (default: 1)
      -D REPEAT_DELAY_MS, --repeat-delay-ms REPEAT_DELAY_MS
                            Sets delay between recorded keystroke sequence
                            repetitions, in milliseconds (only used if --playback-
                            type is repeat-n or repeat-forever) (default: 0)
      -d EVENT_DELAY_MS, --event-delay-ms EVENT_DELAY_MS
                            Sets delay between individual keystroke events, in
                            milliseconds (only used if --maintain-timing is False)
                            (default: 0)
      -m, --maintain-timing
                            Maintain timing between recorded keystrokes (default:
                            False)
      -r RECORD_SECONDS, --record-seconds RECORD_SECONDS
                            Record for this many seconds, instead of recording
                            until Ctrl-C is seen (default: None)
      -s, --translate-scan-codes
                            Attempt to translate PS/2 scan codes to USB HID usage
                            ID codes (default: True)
      -q, --quiet-keypresses
                            Don't print detected keypresses to the terminal
                            (default: False)


Example Digispark sketch generated by keystroke_transcriber
-----------------------------------------------------------

For this example, I ran keystroke_transcriber with the following arguments to record
keypress events until Ctrl+C, and generate a sketch for Digispark devices:

::

    python -m keystroke_transcriber -p oneshot -t digispark -m

Then, I performed the following keyboard / mouse actions:

#. Typed Ctrl+R (to open the "run" program)
#. Typed "notepad", followed by the Enter key (to open the Notepad application)
#. Typed "Hello, from keystroke_transcriber!!!!"
#. Clicked on the terminal window in which I originally ran keystroke_transcriber, to
   bring it back into focus, so the Ctrl+C signal in the next step would be sent to the
   program, instead of being sent to notepad (This step will not be recorded/replicated by
   keystroke_transcriber, since keystroke_transcriber records keypresses and not mouse activity,
   but it is necessary to successfully record keystroke sequences so I am including it anyway)
#. Typed Ctrl+C (to stop the recording)

After I pressed Ctrl+C, keystroke_transcriber provided the following Digispark sketch:

::

    // Auto-generated by keystroke_transcriber. Do not modify!

    #include "DigiKeyboard.h"

    #define NUM_EVENTS (83u)

    // Holds all information required to replay a single keypress
    struct key_event
    {
        uint8_t key;
        uint8_t mods;
        uint16_t delay_before_ms;
    };

    // Holds a sequence of one or more keypress events to be replayed
    const struct key_event key_events[NUM_EVENTS] PROGMEM =
    {
        {0, MOD_GUI_LEFT, 0u}, {21u, MOD_GUI_LEFT, 235u}, {0, MOD_GUI_LEFT, 95u},
        {0, 0, 47u}, {17u, 0, 284u}, {18u, 0, 46u}, {0, 0, 108u}, {23u, 0, 16u},
        {8u, 0, 79u}, {0, 0, 78u}, {19u, 0, 47u}, {0, 0, 94u}, {4u, 0, 61u},
        {7u, 0, 47u}, {0, 0, 93u}, {40u, 0, 188u}, {0, 0, 31u},
        {0, MOD_SHIFT_LEFT, 710u}, {11u, MOD_SHIFT_LEFT, 141u}, {0, 0, 143u},
        {8u, 0, 15u}, {0, 0, 63u}, {15u, 0, 15u}, {0, 0, 62u}, {15u, 0, 31u},
        {18u, 0, 47u}, {0, 0, 142u}, {54u, 0, 63u}, {44u, 0, 46u}, {0, 0, 78u},
        {9u, 0, 15u}, {0, 0, 94u}, {21u, 0, 78u}, {0, 0, 47u}, {18u, 0, 31u},
        {16u, 0, 78u}, {44u, 0, 94u}, {0, 0, 125u}, {14u, 0, 94u}, {0, 0, 48u},
        {8u, 0, 78u}, {0, 0, 31u}, {28u, 0, 62u}, {0, 0, 47u}, {22u, 0, 63u},
        {0, 0, 31u}, {23u, 0, 173u}, {21u, 0, 30u}, {0, 0, 95u}, {18u, 0, 62u},
        {14u, 0, 31u}, {0, 0, 79u}, {8u, 0, 30u}, {0, 0, 94u},
        {0, MOD_SHIFT_LEFT, 46u}, {45u, MOD_SHIFT_LEFT, 31u},
        {0, MOD_SHIFT_LEFT, 31u}, {0, 0, 62u}, {23u, 0, 141u}, {21u, 0, 61u},
        {4u, 0, 108u}, {0, 0, 140u}, {17u, 0, 15u}, {22u, 0, 31u}, {0, 0, 47u},
        {6u, 0, 111u}, {0, 0, 94u}, {21u, 0, 46u}, {0, 0, 47u}, {12u, 0, 31u},
        {5u, 0, 95u}, {0, 0, 139u}, {8u, 0, 46u}, {21u, 0, 47u}, {0, 0, 109u},
        {0, MOD_SHIFT_LEFT, 62u}, {30u, MOD_SHIFT_LEFT, 46u},
        {0, MOD_SHIFT_LEFT, 63u}, {30u, MOD_SHIFT_LEFT, 93u},
        {0, MOD_SHIFT_LEFT, 31u}, {30u, MOD_SHIFT_LEFT, 31u},
        {0, MOD_SHIFT_LEFT, 46u}, {0, 0, 15u}
    };

    // Send a single keypress event to the USB host
    void send_key_event(const struct key_event *event)
    {
        // millis() timestamp of the last sent event
        static unsigned long last_event_time_ms = 0u;

        unsigned long elapsed_ms = millis() - last_event_time_ms;

        if (event->delay_before_ms > elapsed_ms)
        {
        DigiKeyboard.delay(event->delay_before_ms - elapsed_ms);
    }

    last_event_time_ms = millis();
    DigiKeyboard.sendKeyPress(event->key, event->mods);

    }

    // Replay all keypress events stored in PROGMEM
    void replay_key_events()
    {
        for (unsigned i = 0u; i < NUM_EVENTS; i++)
        {
            struct key_event event;

            event.key = pgm_read_byte_near(&key_events[i].key);
            event.mods = pgm_read_byte_near(&key_events[i].mods);
            event.delay_before_ms = pgm_read_word_near(&key_events[i].delay_before_ms);

            send_key_event(&event);
        }
    }

    void setup()
    {
        replay_key_events();
    }

    void loop()
    {

        DigiKeyboard.update();
    }

If you flash this sketch on to your Digispark, and plug the Digispark into a Windows
PC, you will see the keyboard activity I just described, complete with the timing of my original keypresses.