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