Hook and simulate keyboard events on Windows and Linux
—
Advanced text input processing including word detection, automatic text replacement, typed string extraction, and abbreviation expansion. The keyboard package provides sophisticated text processing capabilities for creating text automation tools and improving typing efficiency.
Monitor typed words and trigger callbacks when specific words are detected.
def add_word_listener(word, callback, triggers=['space'], match_suffix=False, timeout=2):
"""
Invokes a callback every time a sequence of characters is typed (e.g. 'pet')
and followed by a trigger key (e.g. space). Modifiers are ignored.
Parameters:
- word: The typed text to be matched (case sensitive)
- callback: Function to call when word is detected (no arguments)
- triggers: List of keys that trigger word matching (default: ['space'])
- match_suffix: If True, match word endings rather than whole words
- timeout: Maximum seconds between characters before discarding current word
Returns:
Function to remove the word listener
Examples:
- add_word_listener('hello', greet_user)
- add_word_listener('help', show_help, triggers=['space', 'enter'])
- add_word_listener('pet', handle_pet, match_suffix=True) # matches 'carpet'
"""
def register_word_listener(word, callback, triggers=['space'], match_suffix=False, timeout=2):
"""Alias for add_word_listener()."""
def remove_word_listener(word_or_handler):
"""
Removes a previously registered word listener. Accepts either the word used
during registration (exact string) or the event handler returned by
add_word_listener().
Parameters:
- word_or_handler: Word string or handler function to remove
"""Automatically replace typed abbreviations with expanded text.
def add_abbreviation(source_text, replacement_text, match_suffix=False, timeout=2):
"""
Registers a hotkey that replaces one typed text with another. The
replacement is done by sending backspace events to delete the source
text, then typing the replacement.
Parameters:
- source_text: Text to be replaced when typed
- replacement_text: Text to replace it with
- match_suffix: If True, match endings of words instead of whole words
- timeout: Maximum seconds between characters before discarding current word
Returns:
Function to remove the abbreviation
Examples:
- add_abbreviation('tm', '™')
- add_abbreviation('addr', '123 Main St, City, State 12345')
- add_abbreviation('email', 'user@example.com')
"""
def register_abbreviation(source_text, replacement_text, match_suffix=False, timeout=2):
"""Alias for add_abbreviation()."""
def remove_abbreviation(word_or_handler):
"""Alias for remove_word_listener()."""Extract meaningful text strings from keyboard event sequences.
def get_typed_strings(events, allow_backspace=True):
"""
Given a sequence of events, tries to deduce what strings were typed.
Strings are separated when a non-textual key is pressed (such as tab or
enter). Characters are converted to uppercase according to shift and
capslock status.
Parameters:
- events: Sequence of KeyboardEvent objects
- allow_backspace: If True, backspaces remove the last character typed
Yields:
str: Extracted text strings
Note: This function is a generator, so you can pass an infinite stream of
events and convert them to strings in real time. This is merely a heuristic
as it cannot access per-process keyboard state like actual keyboard layout.
Example:
get_typed_strings(record()) #-> ['This is what', 'I recorded', '']
"""import keyboard
def greet():
print('Hello there!')
def farewell():
print('Goodbye!')
def help_command():
print('Available commands: hello, bye, help')
# Register word listeners
keyboard.add_word_listener('hello', greet)
keyboard.add_word_listener('bye', farewell)
keyboard.add_word_listener('help', help_command)
print('Type "hello", "bye", or "help" followed by space.')
print('Press ESC to exit.')
keyboard.wait('esc')
keyboard.remove_word_listener('hello')
keyboard.remove_word_listener('bye')
keyboard.remove_word_listener('help')import keyboard
# Common abbreviations
keyboard.add_abbreviation('addr', '123 Main Street, Anytown, ST 12345')
keyboard.add_abbreviation('phone', '+1 (555) 123-4567')
keyboard.add_abbreviation('email', 'john.doe@example.com')
keyboard.add_abbreviation('sig', '\n\nBest regards,\nJohn Doe\nSoftware Engineer')
# Special characters
keyboard.add_abbreviation('tm', '™')
keyboard.add_abbreviation('copy', '©')
keyboard.add_abbreviation('reg', '®')
# Date/time shortcuts
import datetime
now = datetime.datetime.now()
keyboard.add_abbreviation('date', now.strftime('%Y-%m-%d'))
keyboard.add_abbreviation('time', now.strftime('%H:%M:%S'))
print('Abbreviation system active!')
print('Try typing: addr, phone, email, sig, tm, copy, reg, date, time')
print('Press ESC to exit.')
keyboard.wait('esc')import keyboard
import re
class SmartTextProcessor:
def __init__(self):
self.abbreviations = {}
self.word_handlers = {}
self.setup_default_processing()
def setup_default_processing(self):
# Smart capitalization
keyboard.add_word_listener('i', self.capitalize_i, triggers=['space', '.', '!', '?'])
# Auto-correct common mistakes
self.add_smart_abbreviation('teh', 'the')
self.add_smart_abbreviation('adn', 'and')
self.add_smart_abbreviation('recieve', 'receive')
self.add_smart_abbreviation('occured', 'occurred')
# Smart punctuation
keyboard.add_word_listener('--', self.em_dash)
keyboard.add_word_listener('...', self.ellipsis)
def capitalize_i(self):
"""Auto-capitalize standalone 'i'."""
keyboard.send('backspace')
keyboard.write('I')
def em_dash(self):
"""Replace -- with em dash."""
keyboard.send('backspace, backspace')
keyboard.write('—')
def ellipsis(self):
"""Replace ... with proper ellipsis."""
keyboard.send('backspace, backspace, backspace')
keyboard.write('…')
def add_smart_abbreviation(self, wrong, correct):
"""Add abbreviation with smart capitalization."""
def replace_with_case():
# Simple case handling - could be more sophisticated
keyboard.send('backspace' * len(wrong))
keyboard.write(correct)
self.abbreviations[wrong] = keyboard.add_abbreviation(wrong, correct)
# Also handle capitalized version
wrong_cap = wrong.capitalize()
correct_cap = correct.capitalize()
self.abbreviations[wrong_cap] = keyboard.add_abbreviation(wrong_cap, correct_cap)
def cleanup(self):
"""Remove all text processing."""
for remove_func in self.abbreviations.values():
remove_func()
# Usage
processor = SmartTextProcessor()
print('Smart text processing active!')
print('Try typing: i am, teh, adn, recieve, occured, --, ...')
print('Press ESC to exit.')
keyboard.wait('esc')
processor.cleanup()import keyboard
import json
import os
class AbbreviationManager:
def __init__(self, config_file='abbreviations.json'):
self.config_file = config_file
self.active_abbreviations = {}
self.load_abbreviations()
self.setup_management_hotkeys()
def load_abbreviations(self):
"""Load abbreviations from config file."""
if os.path.exists(self.config_file):
with open(self.config_file, 'r') as f:
abbrevs = json.load(f)
for short, full in abbrevs.items():
self.add_abbreviation(short, full)
print(f'Loaded {len(abbrevs)} abbreviations')
def save_abbreviations(self):
"""Save current abbreviations to config file."""
abbrevs = {}
# Extract abbreviation data (simplified)
with open(self.config_file, 'w') as f:
json.dump(abbrevs, f, indent=2)
def add_abbreviation(self, short, full):
"""Add a new abbreviation."""
if short in self.active_abbreviations:
self.remove_abbreviation(short)
remove_func = keyboard.add_abbreviation(short, full)
self.active_abbreviations[short] = {
'full': full,
'remove_func': remove_func
}
print(f'Added abbreviation: {short} -> {full}')
def remove_abbreviation(self, short):
"""Remove an abbreviation."""
if short in self.active_abbreviations:
self.active_abbreviations[short]['remove_func']()
del self.active_abbreviations[short]
print(f'Removed abbreviation: {short}')
def list_abbreviations(self):
"""List all active abbreviations."""
print('Active abbreviations:')
for short, data in self.active_abbreviations.items():
print(f' {short} -> {data["full"]}')
def setup_management_hotkeys(self):
"""Set up hotkeys for managing abbreviations."""
keyboard.add_hotkey('ctrl+alt+a', self.interactive_add)
keyboard.add_hotkey('ctrl+alt+r', self.interactive_remove)
keyboard.add_hotkey('ctrl+alt+l', self.list_abbreviations)
def interactive_add(self):
"""Interactively add abbreviation."""
print('\n=== Add Abbreviation ===')
short = input('Enter abbreviation: ').strip()
if not short:
return
full = input('Enter full text: ').strip()
if not full:
return
self.add_abbreviation(short, full)
def interactive_remove(self):
"""Interactively remove abbreviation."""
print('\n=== Remove Abbreviation ===')
self.list_abbreviations()
short = input('Enter abbreviation to remove: ').strip()
if short:
self.remove_abbreviation(short)
def cleanup(self):
"""Clean up all abbreviations."""
keyboard.unhook_all_hotkeys()
for data in self.active_abbreviations.values():
data['remove_func']()
# Usage
manager = AbbreviationManager()
# Add some default abbreviations
manager.add_abbreviation('brb', 'be right back')
manager.add_abbreviation('omw', 'on my way')
manager.add_abbreviation('lol', 'laugh out loud')
print('Abbreviation manager active!')
print('Ctrl+Alt+A: Add abbreviation')
print('Ctrl+Alt+R: Remove abbreviation')
print('Ctrl+Alt+L: List abbreviations')
print('Press ESC to exit.')
keyboard.wait('esc')
manager.cleanup()import keyboard
from collections import Counter
import re
def analyze_typing_patterns():
"""Analyze typing patterns from a recording."""
print('Type some text for analysis. Press ESC when done.')
events = keyboard.record()
# Extract typed strings
typed_strings = list(keyboard.get_typed_strings(events))
full_text = ' '.join(typed_strings)
print(f'\n=== Typing Analysis ===')
print(f'Full text: "{full_text}"')
print(f'Total strings: {len(typed_strings)}')
print(f'Total characters: {len(full_text)}')
# Word frequency analysis
words = re.findall(r'\b\w+\b', full_text.lower())
word_freq = Counter(words)
print(f'\nWord frequency:')
for word, count in word_freq.most_common(10):
print(f' {word}: {count}')
# Character frequency
char_freq = Counter(c.lower() for c in full_text if c.isalpha())
print(f'\nCharacter frequency:')
for char, count in char_freq.most_common(10):
print(f' {char}: {count}')
# Suggest abbreviations for common phrases
print(f'\nSuggested abbreviations:')
phrases = re.findall(r'\b\w+\s+\w+\b', full_text.lower())
phrase_freq = Counter(phrases)
for phrase, count in phrase_freq.most_common(5):
if count > 1 and len(phrase) > 10:
abbrev = ''.join(word[0] for word in phrase.split())
print(f' "{phrase}" -> "{abbrev}" (used {count} times)')
# Run analysis
analyze_typing_patterns()import keyboard
import time
class ContextualReplacements:
def __init__(self):
self.contexts = {}
self.current_context = 'default'
self.setup_contexts()
def setup_contexts(self):
"""Set up different contexts for text replacement."""
# Programming context
self.contexts['programming'] = {
'fn': 'function',
'ret': 'return',
'var': 'variable',
'cls': 'class',
'imp': 'import',
'def': 'definition'
}
# Email context
self.contexts['email'] = {
'ty': 'Thank you',
'br': 'Best regards',
'fyi': 'For your information',
'asap': 'as soon as possible',
'mtg': 'meeting',
'fup': 'follow up'
}
# Default context
self.contexts['default'] = {
'btw': 'by the way',
'imo': 'in my opinion',
'afaik': 'as far as I know',
'tbh': 'to be honest'
}
self.activate_context('default')
self.setup_context_switching()
def activate_context(self, context_name):
"""Activate a specific context."""
if context_name not in self.contexts:
print(f'Unknown context: {context_name}')
return
# Remove current abbreviations
if hasattr(self, 'active_abbrevs'):
for remove_func in self.active_abbrevs.values():
remove_func()
# Add new context abbreviations
self.active_abbrevs = {}
self.current_context = context_name
for abbrev, full in self.contexts[context_name].items():
remove_func = keyboard.add_abbreviation(abbrev, full)
self.active_abbrevs[abbrev] = remove_func
print(f'Activated {context_name} context with {len(self.contexts[context_name])} abbreviations')
def setup_context_switching(self):
"""Set up hotkeys for context switching."""
keyboard.add_hotkey('ctrl+1', lambda: self.activate_context('default'))
keyboard.add_hotkey('ctrl+2', lambda: self.activate_context('programming'))
keyboard.add_hotkey('ctrl+3', lambda: self.activate_context('email'))
def cleanup(self):
"""Clean up all abbreviations and hotkeys."""
if hasattr(self, 'active_abbrevs'):
for remove_func in self.active_abbrevs.values():
remove_func()
keyboard.unhook_all_hotkeys()
# Usage
contextual = ContextualReplacements()
print('Contextual text replacement active!')
print('Ctrl+1: Default context (btw, imo, afaik, tbh)')
print('Ctrl+2: Programming context (fn, ret, var, cls, imp, def)')
print('Ctrl+3: Email context (ty, br, fyi, asap, mtg, fup)')
print('Press ESC to exit.')
keyboard.wait('esc')
contextual.cleanup()Text processing may encounter:
The package handles most errors gracefully but may miss text detection in edge cases or high-load situations.
Install with Tessl CLI
npx tessl i tessl/pypi-keyboard