I am running Python 3.8 (Also tested on 2.7). Attached below is code to a keylogger that I created with reference to a video tutorial as I'm fairly new to Python and trying to learn. I am trying to make it where when the space key is pressed, it writes a new line to the file so it tabs down and looks nicer. I've tried a few different things online that I've found however nothing has fixed it. If someone could help me and explain why this doesn't work it would be much appreciated. Thanks and have a great week
# Define imports
import pynput
from pynput.keyboard import Key, Listener
# Define variables for keylogger
count = 0
keys = []
# Function to detect key presses
def on_press(key):
global count, keys
keys.append(key)
count += 1
print(str(key))
if count >= 1:
write_file(str(keys))
keys = []
count = 0
# Function to write the letters to a file
def write_file(keys):
with open("log_test.txt", "a") as f:
for key in keys:
k = str(key).replace("'", "").replace("u", "").replace("]", "").replace(",", "").replace("[", "")
if k.find("space") >= 0: # This is the code to check for space bar press
f.write('\n')
else:
k.find("Key") == -1
f.write(k)
# Detect when a key is released
def on_release(key):
if key == Key.esc:
return False
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
That's because your "k" is not "space", but "s", "p", "a", "c", "e".
Not the most elegant method, but try this:
def on_press(key):
global count, keys
keys.append(key)
count += 1
if count >= 1:
write_file(keys) # don't convert to string here
keys = []
count = 0
def write_file(key):
with open("log_test.txt", "a") as f:
if str(key).find("space") >= 0: # transform to string to find what you want
f.write('\n')
elif str(key).find("Key") == -1: # transform to string to find what you want
# key will come as a list, like this: ['char']
# take the first (and only) element, and it will be like this: 'char'
# then remove the "'" and you'll have your character
key = str(key[0]).replace("'", '') # take only the character, then save it
f.write(key)
When you are checking for space, do this:
if k.find(" ") >= 0: # use plain space " " and not "space"
f.write('\n')
Related
I have created a program that prints a character whenever that character is pressed but when i press "backspace" button to remove the last entered character then that character does not get removed
How to integrate backspace functionality in a program so while typing when i press backspace then the last character should get removed?
from dataclasses import field
from pynput import keyboard
import os
import sys
def on_press(key):
try:
#print('Alphanumeric key pressed: {0} '.format(
# key.char))
file.write('{0}'.format(key.char))
except AttributeError:
#print('special key pressed: {0}'.format(
# key))
#file.write('\nSpecial key pressed: {0}'.format(key))
if(key == keyboard.Key.enter):
file.write("\n")
elif(key == keyboard.Key.space):
file.write(" ")
elif(key == keyboard.Key.esc):
file.write("\nLogging Ended")
elif(key == keyboard.Key.backspace):
#Backspace code
def on_release(key):
#print('Key released: {0}'.format(
# key))
if key == keyboard.Key.esc:
# Stop listener
file.close()
return False
# Collect events until released
with keyboard.Listener(
on_press=on_press,
on_release=on_release) as listener:
save_path = os.path.join(os.path.join(os.environ['USERPROFILE']), 'Desktop')
file_name = "A Text.txt"
completeName = os.path.join(save_path, file_name)
try:
file = open(completeName,'x')
except:
file = open(completeName, "a")
file.write("\n\n")
listener.join()
What you want to do is possible, but kind of ugly, because arbitrary relative seeking within a text file is not allowed (since the encoding could easily be variable length, so a .seek(-1, io.SEEK_END), to jump back one character, would involve potentially jumping back 1-4 bytes, and Python won't try to do that).
If you want to make it work, you'll need to do two things:
Don't use 'a' mode for your file (it's fine to have it function as opening for append, but you have to simulate it by opening for write and seeking to the end, so you're allowed to seek within the file)
Before each write to the file, store a cookie for the current position in the file. When you hit backspace, use that cookie to seek to before the write in question and truncate.
This code does all those things and seems to work for me. Comments inline on changes (I deleted unused imports and your own commented out code to keep it succinct).
from pynput import keyboard
import os
from io import SEEK_END # You could just pass 2 as whence, but name is self-documenting
cookies = [] # List of file offset cookies from .tell() calls
def on_press(key):
cookies.append(file.tell())
try:
file.write(f'{key.char}') # Use f-string for shorter/faster code
except AttributeError:
# Deleted unnecessary parens around conditions; Python doesn't need 'em, don't use 'em
if key == keyboard.Key.enter:
file.write("\n")
elif key == keyboard.Key.space:
file.write(" ")
elif key == keyboard.Key.esc:
file.write("\nLogging Ended")
else:
# Any character that doesn't lead to a write should delete the associated
# cookie we just stored
del cookies[-1]
# backspace is a special case of non-writing key
if key == keyboard.Key.backspace:
try:
file.seek(cookies.pop()) # Prior cookie marks position before most
# recent write; seek to before it
file.truncate() # Remove data after that point
except IndexError:
pass # When nothing added yet, backspace does nothing, that's okay
def on_release(key):
if key == keyboard.Key.esc:
# Stop listener
file.close()
return False
# Collect events until released
with keyboard.Listener(
on_press=on_press,
on_release=on_release) as listener:
save_path = os.path.join(os.path.join(os.environ['USERPROFILE']), 'Desktop')
file_name = "A Text.txt"
completeName = os.path.join(save_path, file_name)
try:
file = open(completeName, 'x')
except FileExistsError: # Never use bare except; handle only exceptions you expect
file = open(completeName, 'r+') # Can't use 'a' as it auto-seeks to end of file
# causing problem for manual seeking; don't want 'w'
# because it erases data
file.seek(0, SEEK_END) # 'w' + seeking to end manually fine for our purposes
file.write("\n\n")
listener.join()
For efficiency, it might make sense to only do cookies.append(file.tell()) when you know you're going to write, but frankly, if you're accepting input from a user typing, the I/O involved in potentially overdoing .tell() calls and repeatedly adding/removing an element from cookies is meaningless next to slow human typing.
Note that this design:
Does not allow you to backspace into data from prior runs of the program (the first cookie would correspond to just after the two newlines you insert at the beginning). If you really needed to do so, you could build cookies from any existing file by replacing:
file = open(completeName, 'r+')
file.seek(0, SEEK_END)
with:
file = open(completeName, 'r+')
cookies.append(0)
while c := f.read(1):
cookies.append(f.tell())
file.seek(0, SEEK_END) # Probably not strictly necessary, but nice to be clear all offsets should be at end now
to prepopulate cookies for the existing data.
Treats an output as an atomic unit; right now, the only output that isn't a single character is Esc (which ends the program, so backspaces won't typically follow it, though there is the possibility of holding down Esc then typing backspace I guess), so this won't come up, but if you write multiple characters per keypress, this will delete a whole keypress's worth of output, not just one character. That might be what you want, but if it's not, you'd need to get ugly and do a loop of writing and appending to cookies similar to the one for building cookies from an existing file above.
Your problem most likely lays in that you open your file in 'a' mode (append mode) and therefore you will not be able to change the contents before the previous ending of the file.
If you open the file, read the contents and then again open the file with the 'w' operator (write) you are able to change the contents in which every way you prefer.
# This now stores the full content in a string which can be manipulated.
file_contents = = open(completeName, 'r').read() # must first read content before opening with w to ensure no loss of content
file = open(completeName, 'w')
Then the backspace implementation becomes very easy:
def on_press(key):
try:
#print('Alphanumeric key pressed: {0} '.format(
# key.char))
file_content += f"{key.char}"
except AttributeError:
#print('special key pressed: {0}'.format(
# key))
#file.write('\nSpecial key pressed: {0}'.format(key))
if(key == keyboard.Key.enter):
file_content += "\n"
elif(key == keyboard.Key.space):
file_content += " "
elif(key == keyboard.Key.esc):
file_content += "\nLogging Ended"
elif(key == keyboard.Key.backspace):
file_content = file_content[:-1]
And in the closing argument you instead write the contents to the file and then close it.
def on_release(key):
#print('Key released: {0}'.format(
# key))
if key == keyboard.Key.esc:
# Stop listener
file.write(file_content)
return False
Working Method:
from dataclasses import field
from pynput import keyboard
import os
import sys
output = []
def on_press(key):
try:
output.append(key.char)
except AttributeError:
if(key == keyboard.Key.enter):
output.append('\n')
elif(key == keyboard.Key.space):
output.append(' ')
elif(key == keyboard.Key.backspace):
output.pop()
def on_release(key):
if key == keyboard.Key.esc:
output.append("\nLogging Ended")
return False
# Collect events until released
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
# save_path = os.path.join(os.path.join('~/'), 'Desktop')
file_name = "A Text.txt"
completeName = os.path.join(file_name)
with open(completeName, 'w') as file:
file.write("\n\n")
listener.join()
file.writelines(output)
Basically the headline.
In Python, I want to take max 280 characters of input from the user, and show a live updating counter on the CLI as the user types the input.(Similar to a Progress bar)
Getting some text to update on the screen is simple, but I don't know how to count the characters as the user is inputting them.
P.S. First-time StackOverflow user, please go easy on me. :)
EDIT:
Codebase: https://github.com/Prathamesh-Ghatole/100DaysOfCode-Writer/blob/master/main.py
I don't have a specific code snippet where I want to implement it yet.
But I basically want to take character input from the user in a loop where each iteration does the following:
Take single character input.
update a variable that counts the total number of input characters.
subtract the number of input characters from the character number limit.
Trigger a flag when the character limit is exceeded.
Using pynput package this seems to work.
from pynput.keyboard import Key, Listener
import os
length = 0
char_count = dict()
text = str()
def on_press(key):
try:
global text
global char_count
global length
if length >= 280:
print("[+] Character Limit Excided!")
return False
elif key == Key.enter:
return False
else:
os.system("cls")
if key == Key.space:
text += ' '
else:
text += f"{key.char}"
char_count[text[-1]] = char_count.get(text[-1], 0) + 1
length += 1
print(f"Enter Text: {text}")
print(f"Characters Left: {280 - length}")
print(f"Char Count {char_count}")
except:
exit()
def main():
os.system("cls")
with Listener(on_press=on_press) as listner:
print("Enter Text: ")
listner.join()
if __name__ == '__main__':
main()
The program runs fine, though it won't save my keystrokes to keylogger.txt
from pynput.keyboard import Key, Listener
keys = []
count = 0
def key_pressed(key):
global keys, count
keys.append(key)
count += 1
print(key)
def write_to_file(keys):
with open("keylogger.txt", "a") as f:
for key in keys:
f.write(str(keys))
if count == 1:
count = 0
write_to_file(keys)
keys = []
with Listener(on_press=key_pressed) as listener:
listener.join()
Where is the problem in my code?
Your write_to_file code never runs. You change the value of count but you don't run write_to_file again. Put a write_to_file call inside key_pressed block and it will happen.
I'm not entirely sure of the "buffering" that's happening in your code, but here's how I would do it:
from pynput.keyboard import Key, Listener
def key_pressed(key):
print(key)
with open("keylogger.txt", "a") as f:
f.write(str(key) + "\n")
with Listener(on_press=key_pressed) as listener:
listener.join()
The if statement in your code executes only once, so your write_to_file function is never called.
Problem is this part of your code.
if count == 1:
count = 0
write_to_file(keys)
keys = []
This block in your code never runs.
If you want to use same format of your code use this. But a simpler approach is attached below
from pynput.keyboard import Key, Listener
keys = []
count = 0
def key_pressed(key):
global keys, count
keys.append(key)
count += 1
print(key)
if count == 1:
count = 0
write_to_file(keys)
keys = []
def write_to_file(keys):
with open("keylogger.txt", "a") as f:
for key in keys:
f.write(str(keys))
with Listener(on_press=key_pressed) as listener:
listener.join()
Another implementation for the same
from pynput.keyboard import Key, Listener
def key_pressed(key):
# Stop listener
if key == Key.esc:
return False
with open("keylogger.txt", "a") as f:
f.write(str(key))
with Listener(on_release=key_pressed) as listener:
listener.join()
I am working on making a function which does auto completion based on keystrokes.
Like, in google.com when I hit 'a' in search bar, it shows the related search words such as amazon, amazon prime, airbnb...
I am trying make this function using Python and my CSV file based on detecting keystrokes.
There are some words in the CSV file such as Behavior, Building, Chemistry, History, Manufacturing, Mathematics.
The structure of my code consists of
while # infinitely looping in order to keep detecting keystrokes
for word in data
for char in range(numOfKeyStrokes)
The problem is, when I hit 'c', it is supposed to print out chemistry and it works great.
But when I hit 'h' after I already hit 'c', there is no response.
I want to make this printing out "chemistry" again.
I think when I hit 'h', the compiler doesn't go into "for word in data" statement but I don't know why.
There are some junk print outs in order to detect where the problem is.
Here is my code. Can someone help my problem?
import keyboard
import csv
import time
global numOfKeyStrokes #In order to compare each elemtn's letter one by one with keystroke
numOfKeyStrokes = 0
def keyStroke():
temp = keyboard.read_key()
time.sleep(0.1)
global numOfKeyStrokes
numOfKeyStrokes += 1
return temp
def init():
f = open('./DataBase.csv')
data = csv.reader(f)
getKeyStroke = []
matchingFlag = False
while True: # looping in order to keep getting keystrokes
global numOfKeyStrokes
getKeyStroke.append(keyStroke())
print(getKeyStroke)
print("numOfKeyStrokes ", numOfKeyStrokes)
for word in data: # each row is an each cell in a csv file
matchingFlag = False
for char in range(numOfKeyStrokes): # each x is an each letter in an each cell
print("word[9][char]==getKeyStroke[numOfKeyStrokes-1]", word[9] [char].lower(),",",getKeyStroke[numOfKeyStrokes-1])
if word[9][char].lower() == getKeyStroke[numOfKeyStrokes-1]:
print("char matched")
matchingFlag = True
break
else:
matchingFlag = False
break
if matchingFlag == True : print(word[9])
f.close()
init()
The reason your code only works one time is because you open the csv file once at the beginning of your init function.
When you pass it to the csv.reader and iterate over the entire file, you consume the resource. When you go for the next iteration the file/reader is empty and so your for word in data loop will not run.
I've refactored your program rather significantly, so I don't necessarily know if this still achieves your desired behaviour, especially without knowing exactly how your csv is setup, but I think you were looking for something like:
import csv
import time
import keyboard
def key_stroke_gen():
num_of_key_strokes = 0
while True:
num_of_key_strokes += 1
yield num_of_key_strokes, keyboard.read_key()
time.sleep(0.1)
def init():
get_key_stroke = []
words = []
with open('./DataBase.csv') as f:
data = csv.reader(f)
for row in data:
for word in row:
words.append(word.strip().lower())
for num_of_key_strokes, key_stroke in key_stroke_gen():
get_key_stroke.append(key_stroke)
for word in words:
if word[:num_of_key_strokes] == ''.join(get_key_stroke):
print(word)
if __name__ == '__main__':
init()
Generating the search_str directly in the generator might be a better approach overall, since it's easier to manage the building of the search string in one place and have the processing code separate.
import csv
import time
import keyboard
def search_strs():
search_str = ""
while True:
time.sleep(0.1)
key_press = keyboard.read_key()
if key_press == "backspace" and search_str:
search_str = search_str[:-1]
elif len(key_press) == 1 and ('a' <= key_press <= 'z'):
search_str += keyboard.read_key()
else:
continue
yield search_str
def init():
words = []
with open('./DataBase.csv') as f:
data = csv.reader(f)
for row in data:
for word in row:
words.append(word.strip().lower())
for search_str in search_strs():
for word in words:
if word[:len(search_str)] == search_str:
print(word)
if __name__ == '__main__':
init()
I'm writing a stenography like script, such that, when you press specific combination of keys on your keyboard such as "ASDF" it would translate to a word and type out the word instead of your key presses. However I cannot find a way to override key presses such that programs such as Notepad will ignore me typing "ASDF".
My only current solution to this has been to have my script backspace the characters that were typed, then paste the word that was intended to be typed.
Here is my script thus far:
import ctypes
def main():
GetAsyncKeyState = ctypes.windll.user32.GetAsyncKeyState
key_states = [0] * 26
pressed_keys = [0] * 26
chord = ''
while True:
for k in range(65,91):
key_states[k-65] = GetAsyncKeyState(k)
if GetAsyncKeyState(k): pressed_keys[k-65] = 1
if sum(key_states) == 0:
for k in range(26):
if pressed_keys[k] == 1:
chord += chr(k+65)
if chord != '': write(chord)
chord = ''
pressed_keys = [0] * 26
dictionary = open('dictionary.txt','r').read()
entries = dictionary.split('\n')
chords = [[0,0]]*len(entries)
for e in range(len(entries)):
s = entries[e].split('-')
chords[e] = [s[0],s[1]]
def write(chord):
found = 0
for c in chords:
if chord==c[0]:
found = 1
_type(c[1])
if not found:
_type(chord)
def _type(word):
'''
This is where the code would go...
'''
main()
The expected results would be that none of the characters I'm typing would show up until the script pastes the final word. But instead, my characters that I'm pressing to form the word show up first.
My plan would be to override the keys to do nothing.
I.E. when I pressed "e" nothing would occur.