Kopano Python: Item.move not working (method undefined error)



  • Hello there

    I’m new into kopano python scripting and hope you can help me. I’ modifying the spamd learning service python script for spamassassin mentioned here by @fbartels to be able to

    1. learn ham as well as spam from seperate, configureable folders (working)
    2. move the learned mails after learning (LearnHam -> inbox and LearnSpam -> junk - not working)

    My problem is getting feature 2 to work. I tried to use the move-method on the item according to the documentation, but the line

    item.move(targetfolder)
    

    is always throwing the following error (copied from log): ‘Item’ object has no attribute ‘move’. It’s commented out at the moment as I was unable to figure out what’s wrong and hope you can help me.

    Thank you a lot in advance!
     

    Here is the script (kopano-spamd.py):

    #!/usr/bin/env python
    # coding=utf-8
    """
    ICS driven spam learning daemon for Kopano / SpamAssasin
    See included readme.md for more information.
    """
    import shlex
    import subprocess
    import time
    import kopano
    from MAPI.Tags import *
    from kopano import Config, log_exc
    
    CONFIG = {
    	'run_as_user': Config.string(default="kopano"),
    	'run_as_group': Config.string(default="kopano"),
    	'learncmdspam': Config.string(default="/usr/bin/sudo -u amavis /usr/bin/sa-learn --spam"),
    	'learncmdham': Config.string(default="/usr/bin/sudo -u amavis /usr/bin/sa-learn --ham"),
    	'spamfolder': Config.string(default="LearnSpam"),
    	'hamfolder': Config.string(default="LearnHam")
    }
    
    
    class Service(kopano.Service):
    	def main(self):
    		server = self.server
    		state = server.state
    		catcher = Checker(self)
    		with log_exc(self.log):
    			while True:
    				try:
    					state = server.sync(catcher, state)
    				except Exception as e:
    					if e.hr == MAPI_E_NETWORK_ERROR:
    						self.log.info('Trying to reconnect to Server in %s seconds' % 5)
    					else:
    						self.log.info('Error: [%s]' % e)
    					time.sleep(5)
    				time.sleep(1)
    
    
    class Checker(object):
    	def __init__(self, service):
    		self.log = service.log
    		self.learncmdspam = service.config['learncmdspam']
    		self.learncmdham = service.config['learncmdham']
    		self.spamfolder = service.config['spamfolder']
    		self.hamfolder = service.config['hamfolder']
    
    	def update(self, item, flags):
    		if item.message_class == 'IPM.Note':
    			if item.folder == item.store.user.folder(self.spamfolder):
    				self.learn(item, 'spam')
    			elif item.folder == item.store.user.folder(self.hamfolder):
    				self.learn(item, 'ham')
    
    	def learn(self, item, learntype):
    		with log_exc(self.log):
    			try:
    				spameml = item.eml()
    				if learntype == 'spam':
    					learncmd = self.learncmdspam
    					targetfolder = item.store.user.junk
    				elif learntype == 'ham':
    					learncmd = self.learncmdspam
    					targetfolder = item.store.user.inbox
    				havespam = True
    			except Exception as e:
    				self.log.info('Failed to extract eml of email: [%s] [%s]' % (e, item.entryid))
    			if havespam and learncmd and targetfolder:
    				try:
    					p = subprocess.Popen(shlex.split(learncmd), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    					learning, output_err = p.communicate(spameml)
    					self.log.info('[%s] sa-learn %s: %s' % (item.store.user.name, learntype, learning.strip('\n')))
    					#item.move(targetfolder)
    				except Exception as e:
    					self.log.info('sa-learn failed: [%s] [%s]' % (e, item.entryid))
    
    
    def main():
    	parser = kopano.parser('ckpsF')  # select common cmd-line options
    	options, args = parser.parse_args()
    	service = Service('spamd', config=CONFIG, options=options)
    	service.start()
    
    
    if __name__ == '__main__':
    	main()
    

     
    Here is the config file (spamd.cfg):

    ##############################################################
    # SPAMD SERVICE SETTINGS
    
    # run as specific user
    #run_as_user         = kopano
    
    # run as specific group
    #run_as_group        = kopano
    
    # control pid file
    pid_file            =   /var/run/kopano/spamd.pid
    
    # run server in this path (when not using the -F switch)
    #running_path = /var/lib/kopano
    
    ##############################################################
    # LOG SETTINGS
    
    # Logging method (syslog, file)
    log_method          =   file
    
    # Loglevel (0(none), 1(crit), 2(err), 3(warn), 4(notice), 5(info), 6(debug))
    #log_level           =   3
    
    # Logfile for log_method = file, use '-' for stderr
    log_file            =   /var/log/kopano/spamd.log
    
    # Log timestamp - prefix each log line with timestamp in 'file' logging mode
    log_timestamp       =   1
    
    ###############################################################
    # SPAMD Specific settings
    
    learncmdspam = /usr/bin/sudo -u amavis /usr/bin/sa-learn --spam
    learncmdham = /usr/bin/sudo -u amavis /usr/bin/sa-learn --ham
    spamfolder = LearnSpam
    hamfolder = LearnHam
    

    My environment: Kopano 8.3.1.32 on Univention Corporate Server 4.2-3 errata262



  • @nkauf

    The usage should be like
    move(item, destinationfolder)

    A short example will move an item from the inbox to the drafts folder.

    import kopano
    k=kopano.Server().user('user')
    item = k.store.inbox.store.inbox.items().next()
    k.store.inbox.move(item, k.store.drafts)
    


  • @markb

    Thank you, using with the folder object did the trick. The correct call for my case is

    item.folder.move(item, targetfolder)
    

    I will post the working scrip here: https://help.univention.com/t/zarafa-spamtrain-und-ucs/3785/33



  • @nkauf

    Glad to help,

    I would recommend to put the finished script on github.

    Also it might make sense to rename the script as we will be providing a kopano-spamd with Kopano Core 8.5 which has a slightly different functionality, so to avoid a name clash in the future, see https://jira.kopano.io/browse/KC-666 for more information.

    If you have any more questions about the “old” spamd please ask me as I am the original author.

    Regards

    Mark