Python Keyboard module detects the pressed key more than once - python-3.x

I'm trying to make a keylogger with the 'Keyboard' module in Python, the troubble is that the read_key() function is returning the same pressed key many times.
import keyboard as kb
cadena = ''
while True:
key = kb.read_key()
if len(key) == 1:
cadena +=key
print(cadena)
I just wrote "Hello world"
but this code returns:
h
hh
hhe
hhee
hheel
hheell
hheelll
hheellll
hheellllo
hheelllloo
hheelllloow
hheelllloowo
hheelllloowow
hheelllloowowo
hheelllloowowor
hheelllloowoworr
hheelllloowoworrl
hheelllloowoworrll
hheelllloowoworrlld
hheelllloowoworrlldd

You can use keyboard.record to record all keyboard activity until a specified key is pressed. After this you can either replay the activity or print particular events (e.g. down press events):
import keyboard
text = ""
rec = keyboard.record(until='Enter')
# Either this
keyboard.play(rec)
# Or this
for event in rec:
if event.event_type == "down":
text += event.name
print(text)

Related

InlineKeyboardMarkup Telegram bot (pyTelegramBotAPI)

I am making a bot on pyTelegramBotAPI, I need to generate a list of buttons and write it into the keyboard (So that there are 5 buttons in a row). How should I do it? I have a code:
def start(message):
markup = types.InlineKeyboardMarkup()
buttons = []
for i in range(-12, 15):
if i < 0:
button = types.InlineKeyboardButton(f'{i}', callback_data=f'{i}')
elif i == 0:
button = types.InlineKeyboardButton(f'±{i}', callback_data=f'{i}')
else:
button = types.InlineKeyboardButton(f'+{i}', callback_data=f'{i}')
buttons.append(button)
if len(buttons) == 5:
# here i want to add buttons in a row
bot.send_message(message.chat.id, f'Здравствуйте, <b>{message.from_user.first_name}</b>. Для начала необходимо настроить часовой пояс. Выберите разницу во времени относительно UTC (Москва: "+3", Владивосток: "+10", Исландия: "+1")', parse_mode='html', reply_markup=markup)```
I can advise you to create a separate module responsible for keyboards.
example of modules
This way you can separate the logic of handlers from the logic of keyboards.
As torrua wrote earlier, you can use the keyboa (how to use keyboa) library.
or write the functions yourself to create keyboards and buttons in it.
What you need to know for this:
telebot.types.InlineKeyboardMarkup() # creates an empty keyboard.
telebot.type.InlineKeyboardButton() # creates a button for the keyboard created earlier.
.row() # determines the position of the buttons in the keyboard.
example of function to get keyboards
You can do it easy with keyboa:
from keyboa import Keyboa
def start():
buttons = []
for i in range(-12, 15):
if i < 0:
button = (f'{i}', i)
elif i == 0:
button = (f'±{i}', i)
else:
button = (f'+{i}', i)
buttons.append(button)
keyboard = Keyboa(items=buttons, items_in_row=5).keyboard
bot.send_message(message.chat.id, f'Здравствуйте, <b>{message.from_user.first_name}</b>. Для начала необходимо настроить часовой пояс. Выберите разницу во времени относительно UTC (Москва: "+3", Владивосток: "+10", Исландия: "+1")', parse_mode='html', reply_markup=keyboard)```

Trying to get user input with MessageHandler using ConversationHandler

im trying to deal with both InlineKeyboardButton callback_data, and free text data..here's my scenario:
prompting an InlineKeyboard with several buttons, the user clicks one button and then asked to input some free text for the BE to be used.
I have tried to use CallbackQueryHandler(several InlineKeyboardMarkup in the callback function) as an entry point for ConversationHandler which then trigger MessageHandler, with no much success..
I need to catch the free text update (basically wait for user input).
def start(update, context):
keyboard = [[InlineKeyboardButton("bal bla", callback_data='1'),
InlineKeyboardButton("bla bla", callback_data='2')],
[InlineKeyboardButton("bla bla)", callback_data='3'),
InlineKeyboardButton("bla bla", callback_data= '4')],
[InlineKeyboardButton("bla bla", callback_data='5')]]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
reply_text = {'text': ''}
def reply_message(update, context):
message = update.message.text
reply_text['text'] = message
return reply_text['text']
def button(update, context, user_data):
query = update.callback_query
query.edit_message_text(text="Loading....\n \r")
if query.data == '1':
pass
elif query.data == '2':
pass
elif query.data == '3':
keyboard = [[InlineKeyboardButton('BBB', callback_data='21'),
InlineKeyboardButton('GGG', callback_data='22')],
[InlineKeyboardButton('PPP', callback_data='23')]]
reply_markup1 = InlineKeyboardMarkup(keyboard)
query.edit_message_text('Please select:', reply_markup=reply_markup1)
elif query.data == '21':
query.edit_message_text('input customer name ')
return 1
if : #no idea which condition to give here
print(reply_text['text'], '\n ^ new free text message here ^')
def main():
conv_handler= ConversationHandler(
entry_points=[
CallbackQueryHandler(button)
],
states={
1 : [MessageHandler(Filters.text, reply_message)],
},
fallbacks= []
)
try:
updater = Updater(bot_token, use_context=True)
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(button))
updater.dispatcher.add_handler(conv_handler)
updater.start_polling()
updater.idle()
except Exception as e:
print(e)
Hey i solved this with making a function to parse the user input and remember the input with the user_data method of context.
def check_user_input(update, context):
# get last user text
user_input = update.message.text
# get user_data context
user_data = context.user_data
# set new context user_input
user_data['user_input'] = user_input
context.user_data['user_input'] = user_data['user_input']
if "Text" in user_input:
# do something... i.e reply w/o keyboard
update.message.reply_text(
("{}?!, sounds interesting, tell me more".format(
user_input)))
return GUEST_CHOICE
else:
# ask again
reply_keyboard = [['Text'],['Done']]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
update.message.reply_text(
("{}?!, i dont know anything...".format(
user_input),reply_markup=markup))
return CHECK_CHOICE
This seems to be the favorized method to handle and remember user inputs.
This function can now be called within the ConversationHandler/MessageHandlers as you need. You only have to return to a specified ConversationHandler state. So i will make two states, a GUEST_CHOICE and a CHECK_CHOICE state, to handle a single user input validation as example.
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
# Ask Guest
GUEST_CHOICE: [MessageHandler(Filters.regex('^(Text)$'),
guest_choice),
],
# Verify input
CHECK_CHOICE: [MessageHandler(Filters.text,
check_user_input),
],
},
fallbacks=[MessageHandler(Filters.regex('^Done$'), done, CommandHandler('start', start))],
allow_reentry = False,
per_user=True,
conversation_timeout=3600,
name="test"
)
As defined, /start is used as user to start the conversationhandler. This calls a start() method i.e.:
def start(update, context):
reply_keyboard = [['Text'],['Done']]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
# Setup keyboard and reply
update.message.reply_text(
("Hey {}, how can i help you?".format(
update.message.chat['first_name'])),
reply_markup=markup)
return CHECK_CHOICE
start() will setup the markup keyboard and any input after will be handled with CHECK_CHOICE state, which will call check_user_input here (see above). This will return GUEST_CHOICE and so it will call guest_choice() i.e.:
def guest_choice(update, context):
user_data = context.user_data
# Check authentication request
if "bob" in user_data['user_input']:
update.message.reply_text("Hey {}".format(user_data['user_input']))
else:
update.message.reply_text("Where is bob?!")
return CHECK_INPUT
The Done method will clean up the conversation
def done(update, context):
markup = get_markup(context)
update.message.reply_text("Bye")
return ConversationHandler.END
Ok that escalated quickly... I rewrote that code from my own working version of a conversation handler, please check for bugs :) Maybe the user_data has to be cleared in the Done method.

Error using checkmouse

I'm trying to use checkmouse in order to undraw something from my window. When someone clicks the button it should undraw the text and write something else. I'm using checkMouse and getX() and getY() to do this but i keep receiving this error that states:
File "C:\Users\User\Documents\python\project2.py", line 71, in panel
if (clicknew.getX()>90 and clicknew.getX()<210) and (clicknew.getY()>35 and clicknew.getY() < 0):
AttributeError: 'NoneType' object has no attribute 'getX'
this code that i have done so far is as follows:
from graphics import *
#creating the game panel window
def panel():
#grey window, with coordinates flipped, with banners etc
win = GraphWin("Start Panel", 300,200)
win.setCoords(0,0,300,200)
win.setBackground("light grey")
#drawing the BoilerMazer banner with text
boilermazer = Rectangle(Point(0,200),Point(300,160))
boilermazer.setFill("white")
boilermazer.draw(win)
#text inside
banner1 = Text(Point(150,180),"BoilerMazer")
banner1.setStyle("bold")
banner1.setSize(20)
banner1.draw(win)
#initial game panel is going to have two buttons and a top scores object
#top score "screen"
toprec = Rectangle(Point(60,140),Point(240,50))
toprec.setFill("white")
toprec.draw(win)
#text inside toprec
topscores = Text(Point(150,130),"TOP SCORES")
topscores.setSize(8)
topscores.draw(win)
border = Text(Point(150,120),"======")
border.draw(win)
bigmac = Text(Point(150,110),"Big Mac 21")
bigmac.setSize(8)
bigmac.draw(win)
tt = Text(Point(150,90),"T.T 23")
tt.setSize(8)
tt.draw(win)
cshell = Text(Point(150,75),"C-Shell 25")
cshell.setSize(8)
cshell.draw(win)
macmac = Text(Point(150,55),"MacMac 27")
macmac.setSize(8)
macmac.draw(win)
#new player button that will eventually be clicked
new1 = Point(90,35)
new2 = Point(210,0)
newrec = Rectangle(new1,new2)
newrec.setFill("chartreuse2")
newrec.draw(win)
#new player button text
newplayer = Text(Point(150,18),"NEW PLAYER")
newplayer.draw(win)
#reset button
resetrec = Rectangle(Point(240,35),Point(300,0))
resetrec.setFill("red")
resetrec.draw(win)
#resettext
reset = Text(Point(270,18),"RESET")
reset.draw(win)
#secondary panel window is the game panel after they click new player
#set up points that we check between for the new player button first
#setting up the checkmouse
clicknew = win.checkMouse()
if (clicknew.getX()>90 and clicknew.getX()<210) and (clicknew.getY()>35 and clicknew.getY() < 0):
newplayer.undraw()
you can find the graphics window here:http://mcsp.wartburg.edu/zelle/python/graphics.py
I don't understand what I'm doing wrong, is there some other method that I'm supposed to be using? Thanks for your help
According to the docs, checkMouse() returns None if no mouse click has been detected priorly. So that seems to be the case.
You could put a loop around the call to checkMouse and keep checking if clicknew is not None and only in that case go on in your program. But maybe there's a better way...
UPDATE:
Example:
while True:
clicknew = win.getMouse()
if clicknew:
break
else:
time.sleep(0.1) # avoid busy waiting
# clicknew is set now => use it

after kernel-upgrade: missing keyboard events

After a kernel upgrade (3.19 to 4.4) key map of my keyboard mouse buttons was partially broken.
While others still work as intended, the middle button just fires after key release (then both keydown and keyup).
Tested:
sudo cat /dev/input/event22
sudo evtest /dev/input/event22
sudo showkey
Suggestions? Is there a way to go to a deeper layer than "/dev/input/event22"?
Finally I got a working solution:
sudo cat /dev/usb/hiddev0 | hexdump
did capture the keydown/keypress event!
I didn't dive into the hiddev API, but recognized counter prefixes and repeating suffix values per line. Since they don't differ between keydown and keyup I implemented a toggle function in python (needs read permissions to /dev/usb/hiddev0):
import os
import time
import pyautogui as pa
DEVPATH = "/dev/usb/hiddev0"
BTNHEX = "F200A0FF00000000" # first/unique hex value of ThinkPad middle button
NEWKEY = "ctrlleft" # mapping target
def byteToHex(byteStr):
return "".join(["%02X" % ord(b) for b in byteStr]).strip()
def waitForPathExists(DEVPATH):
while not os.path.exists(DEVPATH): time.sleep(0.5)
def watchHandleDev(dev, isPressed):
byteStr = os.read(dev, 8)
currentHex = byteToHex(byteStr[:8])
if currentHex == BTNHEX:
pa.keyUp(NEWKEY) if isPressed else pa.keyDown(NEWKEY)
return not isPressed
return isPressed
def handleDevUnavailable(dev):
print "device '%s' not readable, waiting" % DEVPATH
os.close(dev)
waitForPathExists(DEVPATH)
print "device '%s' found, reopening" % DEVPATH
return os.open(DEVPATH, os.O_RDONLY)
def main():
dev = os.open(DEVPATH, os.O_RDONLY)
isPressed = False
while True:
try:
isPressed = watchHandleDev(dev, isPressed)
except OSError, err:
print "err", err
if err.errno == 5: dev = handleDevUnavailable(dev)

a single tkinter button for multi function

I am trying to implement a single tkinter button which should "tail -f" some log file in a remote server. It should also stop the process locally AND kill the remote tail process when it is clicked for the second time. I tried this by not using tkinter with success.It stops when ctrl c is pressed.
When the tail(button3) button is clicked it hangs and it waits for the job completed. It is not accepting any new events until then. I know tkinter is single threaded and believe this is causing the issue. Code is below, appreciate any help.
from Tkinter import *
from re import *
import paramiko
import time
import select
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent,width=500,height=500)
self.myContainer1.pack()
#------------------ LABEL #1 for MAC ------------------------------------
#mac label field
self.label = Label (self.myContainer1, text='enter MAC').pack(side=TOP,padx=10,pady=10)
#------------------ ENTRY FIELD #1 for MAC ------------------------------------
#mac entry field
mac_var=StringVar()
self.entry = Entry(self.myContainer1,textvariable= mac_var ,width=10)
self.entry.pack(side=TOP,padx=100,pady=10)
mac_var.set("XXXXX")
s=mac_var.get()
#------------------ LABEL #2 for MAC OWNER ------------------------------------
#name label field
self.label = Label (self.myContainer1, text='enter MAC owner').pack(side=TOP,padx=10,pady=10)
#------------------ ENTRY #2 for MAC OWNER ------------------------------------
#name entry field
name_var=StringVar()
self.entry = Entry(self.myContainer1,textvariable= name_var ,width=25)
self.entry.pack(side=TOP,padx=100,pady=10)
name_var.set("name surname")
s=name_var.get()
#------------------ BUTTON #3 ------------------------------------
# event binding
self.button3 = Button(self.myContainer1)
self.button3.bind("<Button-1>", self.button3Click)
self.button3.configure(text="tail", background="purple")
self.button3.pack(side=LEFT)
def button3Click(self, event):
if self.button3["background"] == "purple":
self.button3.configure(text="Cancel Tail", background="yellow")
self.tail_flag=True
print "tail_flag is" , self.tail_flag
self.taillogg()
else:
self.button3.configure(text="Tail", background="purple")
self.canceltaillogg()
#root.destroy()
def canceltaillogg(self):
self.tail_flag=False
print "tail_flag is" , self.tail_flag
def taillogg(self):
server, port, username, password = ('myserver', 22, 'root', 'passw')
paramiko.util.log_to_file("C:\\log_transport_paramiko.txt")
nbytes = 100
trans = paramiko.Transport((server, port))
trans.connect(username = username, password = password)
trans.set_keepalive(1) # when ssh dies (with Ctrl C) processes spawned by that ssh connections will die, too ( in one sec)
sess = trans.open_channel("session")
#Once the channel is established, we can execute only one command.
#To execute another command, we need to create another channel
sess.exec_command('tail -f /usr/local/var/log/radius/radius.log')
timeout = 10
timestart =time.time()
while True:
try:
rl, wl, xl = select.select([sess],[],[],0.0)
if len(rl) > 0: #stdout
print sess.recv(1024)
if time.time()-timestart > timeout or self.tail_flag==False :
print "timeout 30 sec"
trans.close()
sess.close()
break
except KeyboardInterrupt :
print("Caught Control-C")
trans.close()
sess.close()
break
"""if self.tail_flag==False:
break"""
print ("\n")*100 # clear the screen
print "Starting program"
root = Tk()
root.title('MAC REGISTRATION APPLET')
myapp = MyApp(root)
print ("Ready to start executing the event loop.")
root.mainloop()
print ("Finished executing the event loop.")
Try this - see if it gives you the behavior your want.
self.button3.bind("<Button-1>", lambda:self.root.after(0, self.button3Click)
Also, this is unrelated, but you don't have to call "bind" on your button - it has a built in callback attribute called command:
self.button3 = Button(self.myContainer1, command=lambda:self.root.after(0, self.button3Click))
# self.button3.bind("<Button-1>", self.button3Click) (don't need this)
Then you'll want to remove the event argument from the button3Click function, since the button callback doesn't receive any arguments.

Resources